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

Deadlock after notify_one() (notifying thread locks mutex)

我有以下问题。我有一些执行某些工作的多个线程和一个在工作可用时唤醒它们的主线程。到目前为止,我已经设法使用条件变量和互斥锁编写了一些代码,并且大部分时间都可以正常工作,但有时,通知线程会在调用 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()

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

它是 "condition variable" 而不是 "conditional variable",之所以这样称呼是因为您用它来等待某些条件。

你没有这样做,你只是在等待一个总是 returns 为真的 lambda,这就是你的问题的原因。那以及您的主线程一直持有锁的事实(为什么?!)

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

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

解决方案是不是添加任意睡眠,这不会解决任何问题(永远!)

正确的解决方案是拥有经过测试的条件,并在不更新任何共享数据时停止持有锁。在下面的示例中,正在测试的条件是 "is the boolean ready true?" 并且 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();
}