使用条件变量进行双向通信时的死锁

时间:2014-12-05 05:40:05

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

有谁可以指出我做错了什么? 我希望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);  
    }
}

2 个答案:

答案 0 :(得分: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...)

答案 1 :(得分:0)

好。 main调用notify,然后foo调用notify,然后main锁定互斥锁并等待,然后foo阻塞互斥锁。死锁。