【问题标题】:Deadlock when using condition variables for two way communication使用条件变量进行双向通信时出现死锁
【发布时间】:2015-02-03 05:53:06
【问题描述】:

谁能指出我做错了什么?
我希望 foo 和 bar 交替打印。
有时它会在第一次迭代中挂起,在某些情况下会持续一段时间然后停止。

#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv;
void foo()
{
    while(true)
    {
        std::unique_lock<std::mutex> ul(m);
        cv.wait(ul);
        std::cout<<"bar"<<std::endl;
        ul.unlock();
        cv.notify_one();
    }
}
int main()
{
    std::thread t(foo);
    while(true)
    {
        std::cout<<"foo"<<std::endl;
        cv.notify_one();
        std::unique_lock<std::mutex> ul(m);
        cv.wait(ul);  
    }
}

【问题讨论】:

  • 你忘记了实际的通信!你有一个互斥体,但它不能保护任何东西!

标签: c++ multithreading c++11 mutex deadlock


【解决方案1】:

条件变量仅表示变化,它们本身并不是很有用。你需要把它和一个状态结合起来。

添加另一个变量来指示轮到谁。

std::mutex m;
std::condition_variable cv;
int turn = 0;

void foo()
{
    while(true)
    {
        std::unique_lock<std::mutex> ul(m);
        if( turn == 1 ) {
            // my turn
            std::cout << "bar" << std::endl;

            // tell them it's their turn
            turn = 0;
            cv.notify_one();
        } else {
            // not our turn, wait for a change.
            cv.wait(ul);  
        }
    }
}

int main()
{
    std::thread t(foo);
    while(true)
    {
        std::unique_lock<std::mutex> ul(m);
        if( turn == 0 ) {
            // my turn
            std::cout << "foo" << std::endl;

            // tell them it's their turn
            turn = 1;
            cv.notify_one();
        } else {
            // not our turn, wait for a change.
            cv.wait(ul);  
        }
    }
}

互斥锁用于安全访问turn 变量,每当它发生更改时,您都会通知条件变量,以便其他线程可以唤醒并检查新值。


编辑:假设您了解上述内容,以解决您的难题:
void foo()
{
    std::unique_lock<std::mutex> ul(m);
    while(true)
    {
        std::cout << "bar" << std::endl;
        cv.notify_one();
        cv.wait(ul);  
    }
}

int main()
{
    std::unique_lock<std::mutex> ul(m);
    std::thread t(foo);
    while(true)
    { 
        std::cout << "foo" << std::endl; 
        cv.notify_one();
        cv.wait(ul);
    }
}

换句话说,您只需要在启动子线程之前从循环外部锁定互斥锁,因此首先轮到的逻辑很清楚。然后您执行操作,发出条件信号,然后等待其他线程返回信号。

逻辑流程:

Main Thread             Sub Thread
------------------------------------------
Lock Mutex

Create Subthread

                        Try to lock mutex 
                        but it is busy.

Print "foo"             ...waiting for mutex...

Notify cvar             ignores notification,
                        (still waiting for mutex)

Wait on cvar            Obtains lock
                        (when waiting on a cvar, the lock is released.)

...waiting...           Prints "bar"

Notified, but the       Notify cvar
mutex is still locked           
so we are waiting.

Obtains lock again      Wait on cvar

Print "foo"             ...waiting...

(etc...)

【讨论】:

  • 只需将互斥锁锁定在 main 上,就像您的第二个示例一样工作正常。为什么我应该有额外的转弯标志?
  • 它只是在实际场景中更有用,而第二个示例有效,它根本不灵活
  • 除非你有另一个线程一直在等待通知,否则它不起作用。换句话说,它不是多线程的。
  • 在这种情况下,我试图在 C++ 中实现一个 python 生成器版本。它似乎在没有标志的情况下异步工作。将来可能需要添加标志来表示“生产者”功能的返回。
【解决方案2】:

嗯。 main 调用notify,然后foo 调用notify,然后main 锁定互斥体并等待,然后foo 在互斥体处阻塞。死锁。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-04-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-25
    相关资源
    最近更新 更多