Ваш код имеет несколько проблем с ним.
void RunThread()
{
asking_thread_to_quit = false;
Это условие гонки.Не синхронизируйте неатомарную переменную общего доступа в двух разных потоках без синхронизации.
std::cout << "Started RunThread." << std::endl;
while(true)
{
std::unique_lock<std::mutex> lock(cv_mutex);
std::chrono::seconds delay(5);
Первая using namespace std::literals::chrono_literals;
.Затем используйте 5s
.
if(cv.wait_for(lock, delay, [] { std::cout << "WAKEUP" << std::endl; return asking_thread_to_quit; })) // timed out
{
std::cout << "Breaking RunThread Loop." << std::endl;
break;
}
else
{
std::cout << "TIMER CODE!" << std::endl;
}
, который TIMER CODE
обычно не должен запускаться в пределах блокировки std::mutex
, поскольку это означает, что любой отправляющий сообщение блокируется до тех пор, пока код таймера не будет завершен.
}
}
Наконец, WAKEUP
s являются ложными подробностями.Вы могли бы WAKEUP
50 раз за эти 5 секунд;переменные условия не гарантируют ограниченное количество проверок.
asking_thread_to_quit = true;
cv.notify_all();
это снова приводит к состоянию гонки;Ваша программа теперь выполняет неопределенное поведение дважды.
Изменение asking_thread_to_quit
на std::atomic<bool>
избавит от формального состояния гонки и UB.Однако он позволит вашему коду пропустить запрос на выход и ошибочно выполнить еще 5 секунд ожидания, после чего будет выполнено задание.
Это потому, что может быть вычислено возвращаемое значение вашей лямбды, тогда asking_thread_to_quit=true
иnotify_all
вычисляет ничего, ожидая переменную условия (таким образом, ничего не просыпается), затем переменная условия блокируется, проходит 5 секунд, она возвращается, возвращая false, затем повторяет цикл while.
Смьютекс, удерживаемый во всех записях в bool, запись не может происходить до тех пор, пока не вернется лямбда, и мы ожидаем состояние с разблокированным мьютексом.Это предотвращает пропуск .notify_all()
.
Решающим для этого культом груза является всегда охранник все , считывающие и записывающие asking_thread_to_quit
с помощью cv_mutex
.Затем избегайте удерживать cv_mutex
в течение любого промежутка времени, в том числе при обработке включения таймера.
std::unique_lock<std::mutex> lock_cv() {
return std::unique_lock<std::mutex>(cv_mutex);
}
void RunThread()
{
{
auto lock = lock_cv();
asking_thread_to_quit = false;
}
std::cout << "Started RunThread." << std::endl;
while(true)
{
{
auto lock = lock_cv();
using namespace std::literals::chrono_literals;
if(cv.wait_for(lock, 5s, [] { std::cout << "WAKEUP" << std::endl; return asking_thread_to_quit; })) // timed out
{
std::cout << "Breaking RunThread Loop." << std::endl;
break;
}
}
std::cout << "TIMER CODE!" << std::endl;
}
}
и в основном:
{
auto lock = lock_cv();
asking_thread_to_quit = true;
}
cv.notify_all();
И да, я предназначил для cv.notify_all()
быть вне мьютекса.Оно работает;понимание того, что выходит за рамки решения "Cargo-Cult", которое я здесь предоставляю.
Наконец, WAKEUP
не является ложным.asking_thread_to_quit
мог измениться с момента последней проверки.Запуск лямбды гарантирует, что мы должны уснуть осторожно, без разрыва между разблокировкой мьютекса для ожидания и ожидания уведомлений.
Паразитные WAKEUP
s все еще могут возникать;они будут отображаться как * WAKEUP
с, чем вы ожидаете.