首页 > 解决方案 > 延迟线程启动 - 通知所有未唤醒所有线程

问题描述

尝试:

在多个线程上执行延迟启动。

问题:

我创建了下面的示例来证明这个想法,并尝试在 x 上创建一个竞赛代码来证明所有线程都可以同时运行。

似乎事情是序列化的,而不是并行运行的——这是所需的行为,但也许每个线程运行的时间太短,并且在另一个线程得到服务之前完成

有时一个线程会卡在 cv.wait 上——我在 GDB 中查看过这个,并且可以看到其中一个线程在第 0 帧中等待——这意味着 notify_all 没有唤醒所有线程—— - (这是零星的行为,每隔几次尝试运行二进制文件就会发生)

问:

  1. 使用条件变量是否是一种有效的方法来执行一组线程的延迟启动,这些线程具有它们都将并行运行的所需行为?

  2. 为什么 notify_all() 没有唤醒所有线程?

代码:

// main.cpp
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <atomic>
#include <iostream>
#include <unistd.h>

int main()
{
    std::condition_variable cv;
    std::mutex cv_m;
    int x = 0;
    std::thread t1 = std::thread([&]{std::unique_lock<std::mutex> lk(cv_m); cv.wait(lk); std::cout << "t1 x:" << x++ << std::endl;});
    std::thread t2 = std::thread([&]{std::unique_lock<std::mutex> lk(cv_m); cv.wait(lk); std::cout << "t2 x:" << x++ << std::endl;});
    std::thread t3 = std::thread([&]{std::unique_lock<std::mutex> lk(cv_m); cv.wait(lk); std::cout << "t3 x:" << x++ << std::endl;});
    std::thread t4 = std::thread([&]{std::unique_lock<std::mutex> lk(cv_m); cv.wait(lk); std::cout << "t4 x:" << x++ << std::endl;});
    std::thread t5 = std::thread([&]{std::unique_lock<std::mutex> lk(cv_m); cv.wait(lk); std::cout << "t5 x:" << x++ << std::endl;});
    std::cout << "STARTING" << std::endl;
    cv.notify_all();
    t1.join();
    t2.join();
    t3.join();
    t4.join();
    t5.join();
    std::cout << "DONE" << std::endl;
    return 0;
}

编译:

g++ -std=c++14 main.cpp -lpthread

跑步:

./a.out

标签: c++multithreadingc++14

解决方案


条件变量是无状态的。如果没有服务员,通知会丢失;可以传递spurios 通知。您需要等待共享状态的更改,而不是来自条件变量的信号。

std::condition_variable

当通知条件变量、超时到期或发生虚假唤醒时,线程被唤醒,互斥体被原子地重新获取。如果唤醒是虚假的,线程应该检查条件并继续等待。

此外,在通知条件变量时,如果需要保留服务员 FIFO 顺序,则必须保留互斥锁。

修复:

int main()
{
    std::condition_variable cv;
    std::mutex cv_m;
    int x = 0;
    bool start = false;

    auto thread_fn = [&]{
        std::unique_lock<std::mutex> lk(cv_m);
        while(!start)
            cv.wait(lk);
        std::cout << "t1 x:" << x++ << std::endl;
    };

    std::thread t1 = std::thread(thread_fn);
    std::thread t2 = std::thread(thread_fn);
    std::thread t3 = std::thread(thread_fn);
    std::thread t4 = std::thread(thread_fn);
    std::thread t5 = std::thread(thread_fn);

    std::cout << "STARTING" << std::endl;
    {
        std::unique_lock<std::mutex> lock(cv_m);
        start = true;
        cv.notify_all();
    }

    t1.join();
    t2.join();
    t3.join();
    t4.join();
    t5.join();
    std::cout << "DONE" << std::endl;
}

推荐阅读