C++ 条件等待竞争条件

C++ conditional wait race condition

假设我有一个程序,它有一个工作线程,它对队列中的数字求平方。问题是如果工作很轻(需要很短的时间来完成),worker 完成工作并在它有时间甚至有时间等待 worker 完成之前通知主线程。

我的简单程序如下所示:

#include <atomic>
#include <condition_variable>
#include <queue>
#include <thread>

std::atomic<bool> should_end;

std::condition_variable work_to_do;
std::mutex work_to_do_lock;

std::condition_variable fn_done;
std::mutex fn_done_lock;
std::mutex data_lock;

std::queue<int> work;
std::vector<int> result;

void worker() {
    while(true) {
        if(should_end) return;

        data_lock.lock();
        if(work.size() > 0) {
            int front = work.front();
            work.pop();
            if (work.size() == 0){
                fn_done.notify_one();
            }
            data_lock.unlock();
            result.push_back(front * front);


        } else {
            data_lock.unlock();

            // nothing to do, so we just wait
            std::unique_lock<std::mutex> lck(work_to_do_lock);
            work_to_do.wait(lck);
        }
    }
}


int main() {

    should_end = false;
    std::thread t(worker); // start worker

    data_lock.lock();
    const int N = 10;
    for(int i = 0; i <= N; i++) {
        work.push(i);
    }
    data_lock.unlock();

    work_to_do.notify_one(); // notify the worker that there is work to do
    //if the worker is quick, it signals done here already
    std::unique_lock<std::mutex> lck(fn_done_lock);
    fn_done.wait(lck);

    for(auto elem : result) {
        printf("result = %d \n", elem);
    }

    work_to_do.notify_one(); //notify the worker so we can shut it down
    should_end = true;

    t.join();
    return 0;
}

你试图通过条件变量使用通知本身作为工作完成的标志,这从根本上是有缺陷的。首先也是最重要的是 std::conditional_variable 可能会有虚假的唤醒,所以不应该这样做。您应该使用队列大小作为工作结束的实际条件,在所有线程中保护的相同互斥锁下检查和修改它,并对条件变量使用相同的互斥锁。然后你可以使用 std::conditional_variable 等到工作完成,但是你在检查队列大小之后才这样做,如果工作已经完成,你根本就不会去等待。否则,您将在循环中检查队列大小(由于虚假唤醒)并等待它是否仍不为空,或者您将 std::condition_variable::wait() 与内部具有循环的谓词一起使用。