J'utilise une variable conditionnelle pour arrêter un thread jusqu'à ce qu'un autre thread ait terminé le traitement de sa file d'attente de tâches (longue histoire). Donc, sur un thread, je verrouille et attend:
boost::mutex::scoped_lock lock(m_mutex);
m_condition.wait(lock);
Une fois que l'autre thread a terminé ses tâches, il signale le thread en attente comme suit:
boost::mutex::scoped_lock lock(m_parent.m_mutex);
m_parent.m_condition.notify_one();
Le problème que je vois est que le thread en attente n'arrête pas d'attendre sauf si j'ai défini un point d'arrêt sur les instructions qui le suivent (j'utilise xcode, fyi). Oui, cela semble étrange. Est-ce que quelqu'un sait pourquoi cela pourrait se produire? Suis-je en train de mal utiliser la variable de condition?
Oui, vous utilisez mal la variable de condition. Les "variables de condition" ne sont vraiment que le mécanisme de signalisation. Vous devez également tester une condition. Dans votre cas, il se peut que le thread qui appelle notify_one()
se termine réellement avant que le thread qui appelle wait()
ne démarre même. (Ou du moins, l'appel notify_one()
se produit avant l'appel wait()
.) Cela s'appelle un "réveil manqué".
La solution est d'avoir en fait une variable qui contient la condition qui vous tient à cœur:
bool worker_is_done=false;
boost::mutex::scoped_lock lock(m_mutex);
while (!worker_is_done) m_condition.wait(lock);
et
boost::mutex::scoped_lock lock(m_mutex);
worker_is_done = true;
m_condition.notify_one();
Si worker_is_done==true
Avant que l'autre thread ne commence à attendre, alors vous passerez juste à travers la boucle while sans jamais appeler wait()
.
Ce modèle est si courant que j'irais presque jusqu'à dire que si vous n'avez pas de boucle while
enveloppant votre condition_variable.wait()
alors vous avez toujours un bug. En fait, lorsque C++ 11 a adopté quelque chose de similaire à boost :: condtion_variable, ils ont ajouté un nouveau type de wait () qui prend une expression lambda prédicat (essentiellement, il fait la boucle while
pour vous):
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;});
J'avais mis en œuvre un exemple qui illustre comment utiliser la condition boost, basé sur la discussion.
#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;
}