我正在编写一个事件循环,当没有工作要做时,它会通过等待“要做的事情”条件变量(work_to_do
)进入睡眠状态。可以根据各种事件由不同的线程通知此条件变量。当另一个线程中发生事件时,它会在condition变量上进行通知,唤醒事件循环,然后检查可能触发了notify的条件,循环直到没有更多工作要做,然后再次等待。条件之一由阻止功能(WaitForMessage()
)设置。
事件循环线程:
std::lock_guard<std::mutex> lock(work_to_do_lock);
for (;;) {
if (condition1) {
// Act on condition 1.
} else if (condition2) {
// Act on condition 2.
} else if (HasMessage()) {
// Act on receiving message.
} else {
work_to_do.wait(lock);
}
}
处理来自阻止功能的通知的线程:
for (;;) {
// Wait for message to be received (blocking). Once it returns you are
// guaranteed that HasMessage() will return true.
WaitForMessage();
// Wake-up the main event loop.
work_to_do.notify_one();
}
[主线程在进入事件循环之前在互斥锁上获得了一个保护条件变量(work_to_do_lock
)的锁,并在没有工作要做时将其传递给wait()
调用。为了避免丢失唤醒,通常的建议是,所有通知者在更新其状态时都必须持有该锁。但是,如果要使用WaitForMessage()
来保护work_to_do_lock
调用,则可以防止其他信号唤醒事件循环。
我想出的解决方案是获取并释放锁之后 WaitForMessage()
但在notify_one()
之前:
for (;;) {
// Wait for message to be received (blocking). Once it returns you are
// guaranteed that HasMessage() will return true.
WaitForMessage();
{
std::lock_guard<std::mutex> lock(work_to_do_lock);
}
// Wake-up the main event loop.
work_to_do.notify_one();
}
这应该避免丢失唤醒的问题,因为条件变为真(WaitForMessage()
返回)和notify_one()
在条件检查(HasMessage()
)和wait()
。
另一种方法是不依赖HasMessage()
,而只更新一个共享变量,我们可以使用锁来保护它:
for (;;) {
// Wait for message to be received (blocking). Once it returns you are
// guaranteed that HasMessage() will return true.
WaitForMessage();
{
std::lock_guard<std::mutex> lock(work_to_do_lock);
has_message = true;
}
// Wake-up the main event loop.
work_to_do.notify_one();
}
检查新条件谓词的相应事件循环:
std::lock_guard<std::mutex> lock(work_to_do_lock);
for (;;) {
if (condition1) {
// Act on condition 1.
} else if (condition2) {
// Act on condition 2.
} else if (has_message) {
has_message = false;
// Act on receiving message.
} else {
work_to_do.wait(lock);
}
}
我以前从未见过前一种方法,所以我想知道设计是否存在缺陷或通常避免使用它的原因?看来您可以使用这种方法来锁定条件变量在条件状态更新之前被锁定,假设特定的条件状态写/读本身受到某种互斥机制的保护。
您的方法有效,但效率不如reuses高效,因为同步使得同时调用WaitForMessage
和HasMessage
安全(或者换句话说,需要您work_to_do_lock
更新HasMessage
值,而不是(例如)使用原子)。当然,如果此代码无法访问,这是您能做的最好的事情,因为在其他情况下您需要相互排斥。