可以在同一条件变量上等待多个线程

Can multiple threads wait on the same condition variable?

本文关键字:等待 线程 变量 条件 一条      更新时间:2023-10-16

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

行为。

代码的预期行为是:

  1. 每个线程打印"线程">
  2. 程序等待直到用户按Enter
  3. 当用户按ENTER时,每个线程都会调用notify_one一次
  4. 所有线程打印"线程">

代码的观察到的行为是:

  1. 每个线程打印"线程" n 等待(预期(
  2. 程序等待,直到用户按Enter (预期(
  3. 当用户按ENTER时,每个线程(预期(
  4. notify_one被调用一次
  5. 线程之一打印"线程" n ready&quot&quot&quot&quot&quot of thuts ofers。(???(

问题。

为什么代码悬挂?以及如何在相同条件变量上等待多个线程?

代码

#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.

这是未定义的行为。

为了等待条件变量,条件变量必须通过最初锁定Mutex 的相同的精确线程等待。您不能将MUTEX锁定在一个执行线程中,然后在另一个线程中等待条件变量。

auto lock = std::unique_lock(m);

此锁定在主执行线程中获得。之后,主执行线程创建所有这些多个执行线程。这些执行线程中的每一个执行以下执行:

       cv.wait(lock)

在此处调用wait()的执行线程未获得静音锁,因此这是未定义的行为。

更仔细地看一下您在这里尝试做的事情,这表明您只需移动

即可获得预期的结果
auto lock = std::unique_lock(m);

在每个新执行线程执行的lambda内部。

由于各种竞赛条件,您还需要仅使用notify_all()而不是多次调用notify_one()。请记住,wait()会自动解锁MUTEX并在条件变量上等待,并且wait()仅在线程被条件变量通知后成功地重新锁定MUTEX后才返回。