Я пишу событие l oop, которое засыпает, когда нет работы, ожидающей переменную условия «работа для выполнения» (work_to_do
). Эта переменная условия может быть уведомлена различными потоками на основе различных событий. Когда событие происходит в другом потоке, оно уведомляет об условной переменной, вызывая событие l oop, которое затем проверяет условия, которые могли бы вызвать уведомление, зацикливается до тех пор, пока не останется больше работы, а затем снова ждет. Одно из условий задается функцией блокировки (WaitForMessage()
).
Событие l oop thread:
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
), перед вводом события l oop и передает его в вызов wait()
, когда нет работы для делать. Чтобы избежать потерянных пробуждений, общий совет заключается в том, что все уведомители должны удерживать блокировку при обновлении своих состояний. Однако, если бы вы охраняли вызов WaitForMessage()
с помощью work_to_do_lock
, вы могли бы предотвратить пробуждение других сигналов события l oop.
Решение, которое я придумал, состоит в том, чтобы получить и снять блокировку после 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();
}
Соответствующее событие l oop, которое проверяет новый предикат условия:
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);
}
}
Я никогда не видел первого Подход раньше, поэтому мне было интересно, был ли недостаток в дизайне или причина, по которой его обычно избегали? Кажется, что вы можете использовать этот подход в качестве общей замены для блокировки блокировки условной переменной перед обновлением состояния условия, предполагая, что что указанное c состояние состояния записи / чтения само защищено некоторым механизмом взаимного исключения.