Учитывая условие_вариабель как член класса, я понимаю, что:
- Переменная условия разрушается после завершения деструктора класса.
- Разрушение переменной условия не нужно ждать получения уведомлений.
В свете этих ожиданий, мой вопрос: почему код примера ниже случайно не уведомляет ожидающий поток?
#include <mutex>
#include <condition_variable>
#define NOTIFY_IN_DESTRUCTOR
struct notify_on_delete {
std::condition_variable cv;
~notify_on_delete() {
#ifdef NOTIFY_IN_DESTRUCTOR
cv.notify_all();
#endif
}
};
int main () {
for (int trial = 0; trial < 10000; ++trial) {
notify_on_delete* nod = new notify_on_delete();
std::mutex flag;
bool kill = false;
std::thread run([nod, &flag, &kill] () {
std::unique_lock<std::mutex> lock(flag);
kill = true;
nod->cv.wait(lock);
});
while(true) {
std::unique_lock<std::mutex> lock(flag);
if (!kill) continue;
#ifdef NOTIFY_IN_DESTRUCTOR
delete nod;
#else
nod->cv.notify_all();
#endif
break;
}
run.join();
#ifndef NOTIFY_IN_DESTRUCTOR
delete nod;
#endif
}
return 0;
}
В приведенном выше коде, если NOTIFY_IN_DESTRUCTOR не определен, тест будет выполняться до завершения надежно. Однако при определении NOTIFY_IN_DESTRUCTOR тест будет случайным образом висеть (обычно после нескольких тысяч испытаний).
Я использую Apple Clang: Apple LLVM версия 9.0.0 (clang-900.0.39.2) Цель: x86_64-apple-darwin17.3.0 Модель резьбы: posix С++ 14, скомпилированный с установленными флажками DEBUG.
EDIT:
Чтобы уточнить: этот вопрос касается семантики указанного поведения экземпляров condition_variable. Второй пункт выше, как представляется, закреплен в следующей quote:
Blockquote Требуется: на этом не должно быть потока. [Примечание. То есть все потоки должны быть уведомлены; они могут впоследствии блокировать блокировку, указанную в ожидании. Это расслабляет обычные правила, которые потребовали бы, чтобы все вызовы ожидания произошли до разрушения. Только уведомление о разблокировании ожидания должно произойти до уничтожения. Пользователь должен позаботиться о том, чтобы нити не ждали * этого, как только деструктор был запущен, особенно когда ожидающие потоки вызывают функции ожидания в цикле или используют перегрузки wait, wait_for или wait_until, которые берут предикат. - конечная нота]
Основной семантический вопрос кажется тем, что означает "заблокированный". Моя нынешняя интерпретация приведенной выше цитаты состояла бы в том, что после строки
cv.notify_all(); // defined NOTIFY_IN_DESTRUCTOR
in ~ notify_on_delete() тест потока не "заблокирован на", то есть я понял, что после этого вызова произошло "уведомление о разблокировании ожидания", поэтому согласно цитате требование было выполнено для продолжения уничтожения условия_переменной экземпляра.
Может ли кто-нибудь дать разъяснение "заблокировано" или "уведомление о разблокировании" на то, что в приведенном выше коде вызов notify_all() не удовлетворяет требованиям ~ condition_variable()?