Как использовать переменную состояния boost, чтобы ждать завершения обработки потока?
Я использую условную переменную, чтобы остановить поток, пока другой поток не завершил обработку очереди задач (длинная история). Итак, в одном потоке я блокирую и жду:
boost::mutex::scoped_lock lock(m_mutex);
m_condition.wait(lock);
Как только другой поток выполнит задания, он сигнализирует ожидающий поток следующим образом:
boost::mutex::scoped_lock lock(m_parent.m_mutex);
m_parent.m_condition.notify_one();
Проблема, которую я вижу, заключается в том, что ожидающий поток не останавливается, если я не установил точку останова в последующих инструкциях (я использую xcode, fyi). Да, это кажется странным. Кто-нибудь знает, почему это может произойти? Я неправильно использую переменную условия?
Ответы
Ответ 1
Да, вы неправильно используете переменную условия. "Переменные условий" на самом деле являются всего лишь сигнальным механизмом. Вам также необходимо проверить состояние. В вашем случае может произойти то, что поток, вызывающий notify_one()
, фактически завершается, прежде чем поток, который вызывает wait()
, даже начнет. (Или, по крайней мере, вызов notify_one()
происходит перед вызовом wait()
.) Это называется "пропущенным пробуждением".
Решение состоит в том, чтобы фактически иметь переменную, которая содержит условие, о котором вы заботитесь:
bool worker_is_done=false;
boost::mutex::scoped_lock lock(m_mutex);
while (!worker_is_done) m_condition.wait(lock);
и
boost::mutex::scoped_lock lock(m_mutex);
worker_is_done = true;
m_condition.notify_one();
Если worker_is_done==true
, пока другой поток не начнет ждать, вы просто попадете прямо через цикл while, не назовите wait()
.
Этот шаблон настолько распространен, что я почти зашел так далеко, чтобы сказать, что если у вас нет цикла while
, обертывающего ваш condition_variable.wait()
, тогда у вас всегда есть ошибка. Фактически, когда С++ 11 принял что-то похожее на boost:: condtion_variable, они добавили новый вид wait(), который принимает предикатное лямбда-выражение (по существу, он выполняет цикл while
для вас):
std::condition_variable cv;
std::mutex m;
bool worker_is_done=false;
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return worker_is_done;});
Ответ 2
Я применил пример, который иллюстрирует, как использовать условие boost, основанное на обсуждении.
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
boost::mutex io_mutex;
bool worker_is_done = false;
boost::condition_variable condition;
void workFunction()
{
std::cout << "Waiting a little..." << std::endl;
boost::this_thread::sleep(boost::posix_time::seconds(1));
worker_is_done = true;
std::cout << "Notifying condition..." << std::endl;
condition.notify_one();
std::cout << "Waiting a little more..." << std::endl;
boost::this_thread::sleep(boost::posix_time::seconds(1));
}
int main()
{
boost::mutex::scoped_lock lock(io_mutex);
boost::thread workThread(&workFunction);
while (!worker_is_done) condition.wait(lock);
std::cout << "Condition notified." << std::endl;
workThread.join();
std::cout << "Thread finished." << std::endl;
return 0;
}
Пример переменной условия увеличения