std :: condition_variable :: notify_one()不会唤醒等待的线程

时间:2018-01-12 13:22:07

标签: c++ multithreading condition-variable

我的代码如下:

#include <mutex>
#include <condition_variable>
#include <future>

std::mutex mtx;
std::condition_variable cv;
std::future<void> worker;

void worker_thread() {
  {
    std::lock_guard<std::mutex> lg{ mtx };
    // do something 1
  }
  cv.notify_one();

  // do something 2
}

int main() {
  {
    std::unique_lock<std::mutex> lg{ mtx };
    worker = std::async(std::launch::async, worker_thread);      
    cv.wait(lg);
  }
  // do something 3
}

主线程没有进入// do something 3,我无法理解为什么。我认为在主线程传递cv.notify_one()之后应该从工作线程到达cv.wait(lg)行,所以没有理由挂起。

工作线程负责一些流数据处理,而主线程主要负责GUI事件处理。

// do something 1是关于应该在工作线程内完成的一些初始化。主线程应该等待工作线程完成它。

// do something 2是工作线程的主要工作。

// do something 3是主线​​程的主要工作。

cv.notify_one()更改为cv.notify_all()没有帮助。

条件变量的这种用法是否正确?

2 个答案:

答案 0 :(得分:2)

我必须回溯原来的答案,我为Junekey道歉。我误读了代码,并断定存在竞争条件。我无法重现这个问题。我们需要一个实际上在cv.wait上永久阻塞的例子,以便找出它为什么会这样做。然而,代码是不正确的,如果没有其他原因,它可能会得到虚假通知并在worker_thread调用cv.notify之前通过cv.wait。这很少发生,但它确实发生了。

此代码或多或少是规范的:

#include <mutex>
#include <condition_variable>
#include <thread>

std::mutex mtx;
std::condition_variable cv;
bool worker_done = false;  // <<< puts the "condition" in condition_variable

void worker_thread() {

    // do something 1

    {
        std::lock_guard<std::mutex> lg{ mtx };
        worker_done = true;  // ... and whatever
    }

    cv.notify_one();

    // do something 2
}

int main() {
    std::thread workman(worker_thread);
    {
        std::unique_lock<std::mutex> lg{ mtx };
        while (!worker_done) {
            cv.wait(lg);
        }
    }
    // do something 3
    workman.join();
}

答案 1 :(得分:0)

对不起这个问题完全错了。 我认为“主要线程已经开始等待后通知应该被解雇”仍然是正确的,但是悬挂的根本原因是虚假的觉醒,正如Jive Dadson和ErikAlapää指出的那样。 / p>

有一个原因我无法编译没有优化选项的代码,所以我误解了挂点,因为调试器指向的点不是很清楚。挂起点不是行cv.wait(lg)。它位于// do something 3内。

如果// do something 1成功,我会设置一个标志,如果在// do something 1内抛出异常,则会清除该标志。在// do something 3中,检查标志,如果它指示失败,则主线程调用worker.get()以重新抛出异常。由于// do something 2是一种无限循环形式,如果cv虚假唤醒,主线程就会挂起,因此标志尚未设置。

现在它运作正常!谢谢大家。