notify_one()之后的死锁(通知线程锁互斥锁)

时间:2016-02-25 19:38:59

标签: multithreading c++11 deadlock

我有以下问题。我有一些多线程做一些工作,一个主线程在工作可用时唤醒它们。到目前为止,我已经设法使用条件变量和互斥锁编写了一些代码,并且大部分时间这都可以正常工作,但是通知线程会在调用notify_one()之后立即锁定互斥锁,从而阻止通知的线程和死锁

我写了最少的代码来说明这种情况。

#include <iostream>
#include <thread>
#include <condition_variable>

std::mutex lock;
std::condition_variable cv;

void foo() {
    std::cout << "Thread: Entering doWork()" << std::endl;
    std::unique_lock<std::mutex> l(lock);
    std::cout << "Thread: Acquired lock, going to wait." << std::endl;
    cv.wait(l , []{return true;});
    std::cout << "Thread: Done waiting, exit." << std::endl;
}

int main(void) {
    std::unique_lock<std::mutex> l(lock);
    std::cout << "MAIN: Creating thread." << std::endl;
    std::thread t(foo);
    std::cout << "MAIN: Unlocking mutex." << std::endl;
    l.unlock();
    std::cout << "MAIN: Notifying thread." << std::endl;
    cv.notify_one();
    //std::this_thread::sleep_for(std::chrono::seconds(1));
    l.lock();
    std::cout << "MAIN: Acquired lock." << std::endl;
    std::cout << "MAIN: Joining thread." << std::endl;
    t.join();
    return 0;
}

在理想情况下,输出应为

MAIN: Creating thread.
MAIN: Unlocking mutex.
Thread: Entering doWork()
Thread: Acquired lock, going to wait.
MAIN: Notifying thread.
Thread: Done waiting, exit.
MAIN: Acquired lock.
MAIN: Joining thread.

但往往是

MAIN: Creating thread.
MAIN: Unlocking mutex.
MAIN: Notifying thread.
MAIN: Acquired lock.
MAIN: Joining thread.
Thread: Entering doWork()

除了将睡眠添加到通知线程(我不想做)之外,有没有更好的方法来消除死锁的可能性?提前谢谢。

1 个答案:

答案 0 :(得分:1)

它是“条件变量”而不是“条件变量”,它被调用的原因是你在某些条件下使用它来等待。

你没有这样做,你只是等待一个总是返回true的lambda,这就是问题的原因。那个和你的主线程一直持有锁的事实(为什么?!)

有时main线程在foo线程开始之前快速运行unlock,notify_one和锁定。这意味着foo线程错过了通知,然后尝试获取锁,但不能,因为主线程有它。

条件变量与信号量不同,notify_one`调用不会设置稍后可以检测到的状态。如果在发生notify_one调用时条件变量没有等待,那么它会错过它,它会永远消失。如果你错过了通知然后再睡觉,你将永远不会醒来。

解决方案添加任意睡眠,无法解决任何问题(永远!)

正确的解决方案是拥有一个经过测试的条件,并在您不更新任何共享数据时停止持有锁。在下面的示例中,正在测试的条件是“布尔值ready是真的吗?”并且foo线程将等待该条件为真。主线程设置变量,使条件成立,然后通知另一个线程它应该重新检查条件。

#include <iostream>
#include <thread>
#include <condition_variable>

std::mutex lock;
std::condition_variable cv;
bool ready = false;

void foo() {
    std::cout << "Thread: Entering doWork()" << std::endl;
    std::unique_lock<std::mutex> l(lock);
    std::cout << "Thread: Acquired lock, going to wait." << std::endl;
    cv.wait(l , []{return ready;});
    std::cout << "Thread: Done waiting, exit." << std::endl;
}

int main(void) {
    std::cout << "MAIN: Creating thread." << std::endl;
    std::thread t(foo);
    {
        std::cout << "MAIN: Locking mutex." << std::endl;
        std::unique_lock<std::mutex> l(lock);
        ready = true;
    }
    std::cout << "MAIN: Notifying thread." << std::endl;
    cv.notify_one();
    std::cout << "MAIN: Joining thread." << std::endl;
    t.join();
}