当前位置: 首页 > 知识库问答 >
问题:

带condition_variable的死锁

公孙弘图
2023-03-14

尝试从线程通知condition_variable时出现死锁。

以下是我的MCVE:

#include <iostream>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>

static boost::mutex m_mutex;
static boost::condition_variable m_cond;

void threadFunc()
{
    std::cout << "LOCKING MUTEX" << std::endl;
    boost::mutex::scoped_lock lock( m_mutex );
    std::cout << "LOCKED, NOTIFYING CONDITION" << std::endl;
    m_cond.notify_all();
    std::cout << "NOTIFIED" << std::endl;
}

int main( int argc, char* argv[] )
{
    while( true )
    {
        std::cout << "TESTING!!!" << std::endl;

        boost::mutex::scoped_lock lock( m_mutex );

        boost::thread thrd( &threadFunc );

        //m_cond.wait( lock );
        while ( !m_cond.timed_wait(lock,boost::posix_time::milliseconds(1)) )
        {
            std::cout << "WAITING..." << std::endl;
        }

        static int pos = 0;
        std::cout << "DONE!!! " << pos++ << std::endl;

        thrd.join();
    }

    return 0;
}

如果使用m_cond.wait(lock);,则每次尝试都要编写done!!!,这里没有问题。

如果使用while(!m_cond.timed_wait(lock,boost::posix_time::milliseconds(1)))循环,我会看到已完成!!!正在编写几次尝试,然后在某个时刻,我会得到一个死锁,等待最终永远不会结束:

TESTING!!!
LOCKING MUTEX
LOCKED, NOTIFYING CONDITION
NOTIFIED
WAITING...
WAITING...
WAITING...
WAITING...
WAITING...
WAITING...
...
  • 我在创建线程之前锁定互斥体
  • 则线程无法在到达m_cond.timed_wait之前通知(然后解锁互斥体)
  • 在循环中,如果出现超时,timed_wait重新锁定互斥体,因此无法执行notifice,则打印“Witting...”并且当我们再次准备好接收通知时释放互斥体

那么为什么会出现死锁呢?在timed_wait检测到超时和重新锁定互斥体之间是否可以通知该条件?

共有1个答案

索梓
2023-03-14

问题是,如果timed_wait在调用notify_all之前完成,那么它将不得不等待线程释放互斥体(即,在它调用notify_all之后),然后再继续调用timed_wait,线程已经完成,因此timed_wait将永远不会成功。有两种情况会发生这种情况,如果您的线程需要超过一毫秒才能启动(应该不太可能,但您的OS调度的变化无常意味着它可能发生,特别是如果CPU很忙的话)另一种情况是假唤醒。

通过在调用notify_all时设置一个标志来防止这两种情况,等待线程可以检查该标志以确保notify已被调用:

#include <iostream>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>

static boost::mutex m_mutex;
static boost::condition_variable m_cond;

void threadFunc(bool& notified)
{
    std::cout << "LOCKING MUTEX" << std::endl;
    boost::mutex::scoped_lock lock(m_mutex);
    std::cout << "LOCKED, NOTIFYING CONDITION" << std::endl;
    notified = true;
    m_cond.notify_all();
    std::cout << "NOTIFIED" << std::endl;
}

int main(int argc, char* argv[])
{
    while (true)
    {
        std::cout << "TESTING!!!" << std::endl;

        boost::mutex::scoped_lock lock(m_mutex);

        bool notified = false;

        boost::thread thrd(&threadFunc, boost::ref(notified));

        //m_cond.wait( lock );
        std::cout << "WAITING..." << std::endl;
        while (!m_cond.timed_wait(lock, boost::posix_time::milliseconds(1), [&] { return notified; }))
        {
            std::cout << "WAITING..." << std::endl;
        }

        static int pos = 0;
        std::cout << "DONE!!! " << pos++ << std::endl;

        thrd.join();
    }

    return 0;
}
 类似资料:
  • 我知道互斥体通常也会保护共享数据,使用原子变量只是一个例子。问题不是如何保护共享数据,而是是否需要使用相同的互斥体来保护两者。另一个使用第二互斥体的示例:

  • <condition_variable>头文件提供了条件变量的定义。其作为基本同步机制,允许被阻塞的线程在某些条件达成或超时时,解除阻塞继续执行。 头文件内容 namespace std { enum class cv_status { timeout, no_timeout }; class condition_variable; class condition_variable_

  • 我有一个关于Apache Ignite的问题。我的测试是用一个Ignite服务器(用java编写,使用连续查询来接收更改变量的通知)和一个Ignite客户机(用.NET编写,使用putAll方法发送每50毫秒变化一次的1000个变量的通知)完成的。每个putAll同时发送大约350个变量。我收到的错误是: 2017-07-11 09:56:33,491[警告][网格-超时-工人-#19%null%

  • 今天,我使用Spring Cloud Streams和RabbitMQ,根据本文档编写了以下代码: 我的接口: 和我的属性文件:

  • 编辑:如果不是线程安全的,我正在寻找一个关于竞争条件的描述,这样我就可以更好地理解潜在的问题并填补知识空白。

  • 我不明白的是,当condition_variable和互斥体之间没有任何链接的时候,为什么所有4个线程都可以在互斥体上获得锁?