Предотвращение пробуждения, когда обновление условия является функцией блокировки - PullRequest
2 голосов
/ 14 января 2020

Я пишу событие 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 состояние состояния записи / чтения само защищено некоторым механизмом взаимного исключения.

1 Ответ

1 голос
/ 15 января 2020

Ваш подход работает, но он менее эффективен, чем тот, который повторно использует , независимо от того, какая синхронизация делает безопасным одновременный вызов WaitForMessage и HasMessage (или, иначе говоря, ваш work_to_do_lock требует обновления * Значение 1006 * вместо (скажем) использования атома c). Конечно, если это недоступно для этого кода, это лучшее из того, что вы можете сделать, поскольку вам необходимо взаимное исключение для других условий.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...