Подтвердить что ты не робот

Как использовать переменную состояния 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). Да, это кажется странным. Кто-нибудь знает, почему это может произойти? Я неправильно использую переменную условия?

4b9b3361

Ответ 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;
}

Пример переменной условия увеличения