【问题标题】:Execution not switching between thread (c++11)执行不在线程之间切换(c ++ 11)
【发布时间】:2015-07-04 03:40:27
【问题描述】:

我是 C++11 多线程的初学者。我正在使用小代码并遇到了这个问题。代码如下:

#include <iostream>
#include <thread>
#include <vector>
#include <mutex>

std::mutex print_mutex;

void function1()
{
    std::cout << "Thread1 started" << std::endl;

    while (true)
    {
        std::unique_lock<std::mutex> lock(print_mutex);
        for (size_t i = 0; i<= 1000000000; i++)
            continue;
        std::cout << "This is function1" << std::endl;
        lock.unlock();
    }
}

void function2()
{
    std::cout << "Thread2 started" << std::endl;
    while (true)
    {
        std::unique_lock<std::mutex> lock(print_mutex);
        for (size_t i = 0; i <= 1000000000; i++)
            continue;
        std::cout << "This is function2" << std::endl;
        lock.unlock();
    }
}

int main()
{
    std::thread t1(function1);
    std::thread t2(function2);

    t1.join();
    t2.join();

    return 0;
}

我已经编写了代码,直觉期待以下输出:

Thread1 已启动
Thread2 已启动

这是 function1
这是 function2
这是 function1
。 .
.
.

但是显示的输出如下:

线程 1 已启动
线程 2 已启动

这是函数1

这是函数1
这是 函数1
.
.
.

我哪里错了?

【问题讨论】:

  • 您使用的是哪个编译器?使用 Visual Studio 2013,结果符合预期。
  • 嗯,我认为无法预测这些线程的调度方式,因此我认为第一个输出是完全有效的。您应该在解锁后将其放在循环中以获得所需的输出,但即使那样我认为您也无法保证您将始终获得相同的输出。
  • 我在 ubuntu 14.10 上使用 g++ 4.9.1。
  • 有点离题,但值得一提的是,这两个lock.unlock() 语句是无害的,但完全没有必要。使用std::unique_lock 的全部意义在于,当它超出范围时,它将自动解锁其关联的互斥锁。此外,这两个延迟循环可能会被优化掉。你最好使用std::this_thread::sleep_for()之类的东西。
  • Ferruccio,这对初学者来说是一个很好的优化技巧。是不是当持有锁的线程进入休眠状态时,锁会切换到其他等待线程..??

标签: c++ multithreading c++11 thread-synchronization


【解决方案1】:

您的两个线程都在执行以下步骤:

  • 锁定
  • 长空循环
  • 打印
  • 解锁
  • 锁定
  • 长空循环
  • (等等)

实际上,您没有离开任何时间进行上下文切换,在解锁之后就有一个锁。解决方法:交换“锁”和“长空循环”步骤,这样只有“打印”步骤会被锁定,调度器可以在“长空循环”期间切换到其他线程。

欢迎来到话题!

编辑: 专业提示:调试多线程程序很困难。但有时值得插入一个普通的 printf() 来指示锁定和解锁(正确的顺序:锁定,然后 printf 和 printf 然后解锁),即使程序看起来正确。在这种情况下,您可以看到解锁-锁定之间的零间隙。

【讨论】:

  • 交换的东西工作得很好。正如您所说,调度程序需要时间来切换线程之间的锁,这个时间是可预测的..??因为我需要使用 c++11 进行双缓冲。
  • 你不能依赖交换循环和锁的顺序来始终工作,请参阅this 示例
  • 正如另一个答案所说,你不能确定。根据经验,您只能在很短的时间内锁定事物。另一个好的做法是,如果程序可以实现(例如,它不是时间紧迫的),您可以在“回合”之间使用 sleep(),例如 10-100-1000 毫秒,具体取决于任务(例如,如果您正在观看出现一个文件,使用 1000 毫秒,如果您正在等待另一个任务,使用 100 毫秒睡眠)。
【解决方案2】:

解锁互斥锁并不能保证等待锁定同一个互斥锁的另一个线程会立即获得锁。

它只保证其他线程会尝试获取锁。

在这种情况下,在一个线程中解锁互斥锁后,同一线程将立即尝试再次锁定它。即使另一个线程耐心地等待,对于互斥锁,也不能保证另一个线程这次会赢。刚刚锁定它的同一个线程可以立即再次成功锁定它。

今天,您会看到同一个线程总是会赢得锁定竞赛。明天,您可能会发现它始终是另一个线程。无论如何,当有多个线程同时追求同一个互斥锁时,您无法保证哪个线程将获取该互斥锁。获胜者取决于您的 CPU 和其他硬件架构、系统加载的繁忙程度、时间以及许多其他因素。

【讨论】:

  • 感谢您的解释,您的意思是多线程是不可预测的,即哪个线程获取锁等,?
  • 对。您所拥有的只是保证 some 线程将获得锁。
【解决方案3】:

这是一个有效的结果,您的代码不会试图以任何方式控制执行顺序,只要所有线程在某个时间点执行并且没有问题并且这是一个合法的结果。

即使您切换了循环和锁的顺序(see here),也可能发生这种情况,因为您还没有编写任何尝试使用例如conditional variables 或只是一些silly @987654324 来控制它的东西@(这是一个愚蠢的解决方案,只是为了演示你如何真正让它交替并确保它会)布尔来交替运行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-23
    • 2019-11-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多