与 condition_variable 死锁
dead-lock with condition_variable
我在尝试从线程通知 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)) )
循环,我看到 DONE!!!
被写了几次尝试,并且在某个时候,我遇到了死锁,最终等待永远不会结束:
TESTING!!!
LOCKING MUTEX
LOCKED, NOTIFYING CONDITION
NOTIFIED
WAITING...
WAITING...
WAITING...
WAITING...
WAITING...
WAITING...
...
我已经阅读了关于 Whosebug 的其他帖子(如 Condition variable deadlock):他们提到如果在条件的等待函数 运行 之前调用 notify_all
可能会发生这种情况,因此互斥体必须用于防止这种情况。但我觉得这就是我正在做的事情:
- 我在创建线程之前锁定互斥体
- 然后线程无法在达到
m_cond.timed_wait
之前通知(然后解锁互斥量)
- 在循环中,如果超时,
timed_wait
重新锁定互斥锁,因此无法完成通知,我们打印“WITTING...”,并在我们再次准备好接收通知时释放互斥锁
那么为什么会出现死锁呢?在 timed_wait
检测到超时和重新锁定互斥量之间是否可以通知条件?
必须在发出条件信号之前开始等待条件变量。使用您的代码,虚假唤醒可能允许线程在等待开始之前完成。
解决方案是这样 - 不要只等待条件变量。测试共享标志,并使用条件变量在收到信号后立即唤醒。请在此处查看 Rainer 的指南:
https://www.modernescpp.com/index.php/c-core-guidelines-be-aware-of-the-traps-of-condition-variables
另请参阅此线程,了解如何将 boost 条件变量与谓词一起使用:
boost::condition_variable - using wait_for with predicate
和
How do I use a boost condition variable to wait for a thread to complete processing?
问题是,如果 timed_wait
在调用 notify_all
之前完成,则必须等待线程在调用 notify_all
之前释放互斥量(即在调用 notify_all
之后)它恢复然后将再次调用 timed_wait
,线程已完成,因此 timed_wait
永远不会成功。有两种情况可能会发生这种情况,如果您的线程启动时间超过一毫秒(应该不太可能,但您的 OS 的调度变幻莫测意味着它可能发生,特别是如果 CPU 很忙)另一个是虚假唤醒。
这两种情况都可以通过在调用 notify_all
时设置一个标志来防止,等待线程可以检查该标志以确保已调用通知:
#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
时遇到死锁。
这是我的 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)) )
循环,我看到 DONE!!!
被写了几次尝试,并且在某个时候,我遇到了死锁,最终等待永远不会结束:
TESTING!!!
LOCKING MUTEX
LOCKED, NOTIFYING CONDITION
NOTIFIED
WAITING...
WAITING...
WAITING...
WAITING...
WAITING...
WAITING...
...
我已经阅读了关于 Whosebug 的其他帖子(如 Condition variable deadlock):他们提到如果在条件的等待函数 运行 之前调用 notify_all
可能会发生这种情况,因此互斥体必须用于防止这种情况。但我觉得这就是我正在做的事情:
- 我在创建线程之前锁定互斥体
- 然后线程无法在达到
m_cond.timed_wait
之前通知(然后解锁互斥量) - 在循环中,如果超时,
timed_wait
重新锁定互斥锁,因此无法完成通知,我们打印“WITTING...”,并在我们再次准备好接收通知时释放互斥锁
那么为什么会出现死锁呢?在 timed_wait
检测到超时和重新锁定互斥量之间是否可以通知条件?
必须在发出条件信号之前开始等待条件变量。使用您的代码,虚假唤醒可能允许线程在等待开始之前完成。
解决方案是这样 - 不要只等待条件变量。测试共享标志,并使用条件变量在收到信号后立即唤醒。请在此处查看 Rainer 的指南: https://www.modernescpp.com/index.php/c-core-guidelines-be-aware-of-the-traps-of-condition-variables
另请参阅此线程,了解如何将 boost 条件变量与谓词一起使用: boost::condition_variable - using wait_for with predicate
和 How do I use a boost condition variable to wait for a thread to complete processing?
问题是,如果 timed_wait
在调用 notify_all
之前完成,则必须等待线程在调用 notify_all
之前释放互斥量(即在调用 notify_all
之后)它恢复然后将再次调用 timed_wait
,线程已完成,因此 timed_wait
永远不会成功。有两种情况可能会发生这种情况,如果您的线程启动时间超过一毫秒(应该不太可能,但您的 OS 的调度变幻莫测意味着它可能发生,特别是如果 CPU 很忙)另一个是虚假唤醒。
这两种情况都可以通过在调用 notify_all
时设置一个标志来防止,等待线程可以检查该标志以确保已调用通知:
#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;
}