如果我评论"while"块,为什么程序会陷入僵局?它的"屈服"线如何影响?

Li.*_*Cao 2 c++ deadlock condition-variable

我正在学习condition_variable并运行一些例子.我很好奇,如果我对块进行注释,为什么以下代码会出现死锁.这是使用condition_variable的简单消费者和生产者示例.我认为这是一个僵局问题,不是吗?

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

using namespace std;

mutex mtx;
condition_variable cv;

int cargo = 0;

bool shipment_available()
{
    return cargo != 0;
}

void consume(int cnt)
{
    for (int i = 0; i < cnt; i++)
    {
        unique_lock<mutex> lck(mtx);
        cv.wait(lck, shipment_available);
        printf("%d\n", cargo);
        cargo = 0;
    }
}

int main()
{
    thread consumer_thread(consume, 10);

    for (int i = 0; i < 10; i++)
    {
        //while (shipment_available())    // Dead lock without this block
        //{
        //    std::this_thread::yield();
        //}
        unique_lock<mutex> lck(mtx);
        cargo = i + 1;
        cv.notify_one();
    }

    consumer_thread.join();
}
Run Code Online (Sandbox Code Playgroud)

如果我取消注释块,它运行良好.

Who*_*aig 5

因此,走过这种极有可能的可能性:

  1. main() 启动消费者线程.
  2. 在线程获取互斥main()锁之前,锁定它,递增cargo并触发通知,然后通过scope-rotation释放互斥锁十次.
  3. main()现在运行到join消费者线程.
  4. 消费者最终获得互斥锁,并准备等待给定谓词的条件变量(非零货物).
  5. 谓词已经为真,因此不会执行等待.
  6. 消费者将货物归零,然后通过范围 - 轮换释放互斥锁.
  7. 消费者第二次循环,锁定互斥锁,然后检查谓词,只是现在谓词是假的,因为cargo确实为零,所以它等待条件变量(在过程中释放互斥锁)以获得永远不会出现的信号.唯一发送过该信号的是main(),它只是等待消费者线程加入,这现在永远不会发生.

简而言之,您似乎相信条件变量信号会叠加.事实并非如此.如果在发布通知时没有人正在等待通知,那么它就会丢失给以太.

最后,这很重要,你的"修复"本身就是完全错误的.该shipment_available函数检查谓词数据(即必须由互斥体保护的数据,不仅用于修改,而且用于检查).检查没有锁定互斥锁的main是竞争条件的配方.

我的建议是将货物减少一个而不是将货物归零.或者,您可以在将其归零之前增加icargo,从而加速i上升到cnt极限,但您似乎使用上升货物量,因此您必须相应地进行其他调整.