与 boost::condition_variable 的死锁
Deadlock with boost::condition_variable
我有点被问题困住了,所以这是我的求助。
我有一个管理器将一些事件推送到队列中,该队列在另一个线程中进行。
我不希望这个线程对于队列中的事件是 'busy waiting',因为它可能一直是空的(也可能总是满的)。
我还需要 m_bShutdownFlag
在需要时停止线程。
所以我想在这种情况下尝试 condition_variable
:如果有东西被推送到队列,线程就会开始工作。
简化代码:
class SomeManager {
public:
SomeManager::SomeManager()
: m_bShutdownFlag(false) {}
void SomeManager::Initialize() {
boost::recursive_mutex::scoped_lock lock(m_mtxThread);
boost::thread thread(&SomeManager::ThreadProc, this);
m_thread.swap(thread);
}
void SomeManager::Shutdown() {
boost::recursive_mutex::scoped_lock lock(m_mtxThread);
if (m_thread.get_id() != boost::thread::id()) {
boost::lock_guard<boost::mutex> lockEvents(m_mtxEvents);
m_bShutdownFlag = true;
m_condEvents.notify_one();
m_queue.clear();
}
}
void SomeManager::QueueEvent(const SomeEvent& event) {
boost::lock_guard<boost::mutex> lockEvents(m_mtxEvents);
m_queue.push_back(event);
m_condEvents.notify_one();
}
private:
void SomeManager::ThreadProc(SomeManager* pMgr) {
while (true) {
boost::unique_lock<boost::mutex> lockEvents(pMgr->m_mtxEvents);
while (!(pMgr->m_bShutdownFlag || pMgr->m_queue.empty()))
pMgr->m_condEvents.wait(lockEvents);
if (pMgr->m_bShutdownFlag)
break;
else
/* Thread-safe processing of all the events in m_queue */
}
}
boost::thread m_thread;
boost::recursive_mutex m_mtxThread;
bool m_bShutdownFlag;
boost::mutex m_mtxEvents;
boost::condition_variable m_condEvents;
SomeThreadSafeQueue m_queue;
}
但是当我用两个(或更多)几乎同时调用 QueueEvent
来测试它时,它会永远锁定在 boost::lock_guard<boost::mutex> lockEvents(m_mtxEvents);
行。
似乎第一个调用永远不会释放 lockEvents
,所以其余的都在等待释放。
请帮我找出我做错了什么以及如何解决这个问题。
在 ThreadProc 内部,while(true) 循环,锁 Events 在任何情况下都不会解锁。尝试锁定并在范围内等待。
您的代码有几点需要指出:
- 您可能希望在调用关闭后加入您的线程,以确保您的主线程不会在您的其他线程之前完成。
m_queue.clear();
关闭是在 m_mtxEvents
互斥锁之外完成的,这意味着它不像您认为的那样线程安全。
- 您的 'thread safe processing' 队列应该只是取出一个项目,然后在您去处理事件时释放锁。您没有明确显示,但不这样做将导致锁定阻止添加项目。
关于线程阻塞的好消息是,您可以轻松中断并检查其他线程在做什么,并找到持有锁的线程。根据我的评论 #3,您可能只是花了很长时间来处理一个事件。另一方面,您可能遇到了死锁。无论如何,您需要的是使用调试器来准确确定您做错了什么,因为您的示例中没有足够的内容来证明您的问题。
我有点被问题困住了,所以这是我的求助。
我有一个管理器将一些事件推送到队列中,该队列在另一个线程中进行。
我不希望这个线程对于队列中的事件是 'busy waiting',因为它可能一直是空的(也可能总是满的)。
我还需要 m_bShutdownFlag
在需要时停止线程。
所以我想在这种情况下尝试 condition_variable
:如果有东西被推送到队列,线程就会开始工作。
简化代码:
class SomeManager {
public:
SomeManager::SomeManager()
: m_bShutdownFlag(false) {}
void SomeManager::Initialize() {
boost::recursive_mutex::scoped_lock lock(m_mtxThread);
boost::thread thread(&SomeManager::ThreadProc, this);
m_thread.swap(thread);
}
void SomeManager::Shutdown() {
boost::recursive_mutex::scoped_lock lock(m_mtxThread);
if (m_thread.get_id() != boost::thread::id()) {
boost::lock_guard<boost::mutex> lockEvents(m_mtxEvents);
m_bShutdownFlag = true;
m_condEvents.notify_one();
m_queue.clear();
}
}
void SomeManager::QueueEvent(const SomeEvent& event) {
boost::lock_guard<boost::mutex> lockEvents(m_mtxEvents);
m_queue.push_back(event);
m_condEvents.notify_one();
}
private:
void SomeManager::ThreadProc(SomeManager* pMgr) {
while (true) {
boost::unique_lock<boost::mutex> lockEvents(pMgr->m_mtxEvents);
while (!(pMgr->m_bShutdownFlag || pMgr->m_queue.empty()))
pMgr->m_condEvents.wait(lockEvents);
if (pMgr->m_bShutdownFlag)
break;
else
/* Thread-safe processing of all the events in m_queue */
}
}
boost::thread m_thread;
boost::recursive_mutex m_mtxThread;
bool m_bShutdownFlag;
boost::mutex m_mtxEvents;
boost::condition_variable m_condEvents;
SomeThreadSafeQueue m_queue;
}
但是当我用两个(或更多)几乎同时调用 QueueEvent
来测试它时,它会永远锁定在 boost::lock_guard<boost::mutex> lockEvents(m_mtxEvents);
行。
似乎第一个调用永远不会释放 lockEvents
,所以其余的都在等待释放。
请帮我找出我做错了什么以及如何解决这个问题。
在 ThreadProc 内部,while(true) 循环,锁 Events 在任何情况下都不会解锁。尝试锁定并在范围内等待。
您的代码有几点需要指出:
- 您可能希望在调用关闭后加入您的线程,以确保您的主线程不会在您的其他线程之前完成。
m_queue.clear();
关闭是在m_mtxEvents
互斥锁之外完成的,这意味着它不像您认为的那样线程安全。- 您的 'thread safe processing' 队列应该只是取出一个项目,然后在您去处理事件时释放锁。您没有明确显示,但不这样做将导致锁定阻止添加项目。
关于线程阻塞的好消息是,您可以轻松中断并检查其他线程在做什么,并找到持有锁的线程。根据我的评论 #3,您可能只是花了很长时间来处理一个事件。另一方面,您可能遇到了死锁。无论如何,您需要的是使用调试器来准确确定您做错了什么,因为您的示例中没有足够的内容来证明您的问题。