多个线程可以等待同一个条件变量吗?

Can multiple threads wait on the same condition variable?

我正在尝试了解如何更好地使用条件变量,并且我有以下代码。

行为。

代码的预期行为是:

  1. 每个线程打印“thread n waiting”
  2. 程序一直等到用户按下回车
  3. 当用户按下回车键时,notify_one为每个线程调用一次
  4. 所有线程打印“thread n ready.”,然后退出

代码的观察到的行为是:

  1. 每个线程打印“thread n waiting” (预期)
  2. 程序一直等到用户按下回车(预期)
  3. 当用户按下回车键时,notify_one 为每个线程调用一次 (预期)
  4. 其中一个线程打印“thread n ready”,但随后代码挂起。 (???)

问题。

为什么代码挂了?我怎样才能让多个线程等待同一个条件变量?

代码

#include <condition_variable>
#include <iostream>
#include <string>
#include <vector>
#include <thread>

int main() {

    using namespace std::literals::string_literals; 
    auto m = std::mutex(); 

    auto lock = std::unique_lock(m);

    auto cv = std::condition_variable(); 

    auto wait_then_print =[&](int id) {
        return [&, id]() {
            auto id_str = std::to_string(id); 
            std::cout << ("thread " + id_str + " waiting.\n"); 
            
            cv.wait(lock); 
            // If I add this line in, the code gives me a system error:
            // lock.unlock();

            std::cout << ("thread " + id_str + " ready.\n"); 
        };
    };

    auto threads = std::vector<std::thread>(16); 

    int counter = 0; 
    for(auto& t : threads)
        t = std::thread(wait_then_print(counter++));

    std::cout << "Press enter to continue.\n"; 

    std::getchar(); 

    for(int i = 0; i < counter; i++) {
        cv.notify_one();
        std::cout << "Notified one.\n";
    }

    for(auto& t : threads)
        t.join(); 
}

输出

thread 1 waiting.
thread 0 waiting.
thread 2 waiting.
thread 3 waiting.
thread 4 waiting.
thread 5 waiting.
thread 6 waiting.
thread 7 waiting.
thread 8 waiting.
thread 9 waiting.
thread 11 waiting.
thread 10 waiting.
thread 12 waiting.
thread 13 waiting.
thread 14 waiting.
thread 15 waiting.
Press enter to continue.

Notified one.
Notified one.
thread 1 ready.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.
Notified one.

这是未定义的行为。

为了等待条件变量,条件变量必须由最初锁定互斥体的同一个线程等待.您不能在一个执行线程中锁定互斥锁,然后在另一个线程中等待条件变量。

auto lock = std::unique_lock(m);

这个锁是在主执行线程中获取的。之后,主执行线程创建所有这些多个执行线程。这些执行线程中的每一个都执行以下操作:

       cv.wait(lock)

此处调用wait()的执行线程未获取互斥锁,因此这是未定义的行为。

仔细查看您在此处尝试执行的操作表明,如果您简单地移动,您可能会得到预期的结果

auto lock = std::unique_lock(m);

在每个新执行线程执行的 lambda 中。

由于各种竞争条件,您还需要简单地使用 notify_all() 而不是多次调用 notify_one()。请记住,wait() 自动解锁互斥量并等待条件变量,而 wait() returns 只有在线程被条件变量通知后成功重新锁定互斥量后。