Déblocage parasite dans le thread boost
-
05-07-2019 - |
Question
J'ai trouvé cet intéressant paragraphe dans le Documentation sur les threads Boost :
void wait(boost::unique_lock<boost::mutex>& lock)
...
Effets: appelez atomiquement lock.unlock () et bloque le thread actuel. le le fil se débloquera à la réception d'un message appeler this- > notify_one () ou this- > notify_all () ou de manière fausse . Lorsque le fil est débloqué (pour quelle que soit la raison), le verrou est ré-acquis en appelant lock.lock () avant que l'appel à attendre revienne. le le verrou est également ré-acquis en appelant lock.lock () si la fonction se termine avec une exception.
Donc, ce qui m'intéresse, c'est la signification du mot "de manière fausse". Pourquoi le fil serait-il débloqué pour des raisons fallacieuses? Que peut-on faire pour résoudre ce problème?
La solution
Cet article de Anthony Williams est particulièrement détaillé. .
Des sillages parasites ne peuvent être prédits: ils sont essentiellement aléatoires de la point de vue de l'utilisateur. Cependant, ils se produisent généralement lorsque la bibliothèque de thread ne peut pas assurer de manière fiable qu'une attente thread ne manquera pas une notification. Depuis une notification manquée serait rendre la variable de condition inutile, la bibliothèque de threads réveille le thread de son attente plutôt que de prendre la risque.
Il souligne également que vous ne devez pas utiliser les surcharges timed_wait
qui prennent une durée, et que vous devez généralement utiliser les versions utilisant un prédicat
C'est le bogue du débutant, et un c'est facilement surmonté avec un simple règle: vérifiez toujours votre prédicat dans un boucle en attente avec une condition variable. Le bogue le plus insidieux vient de timed_wait ().
Cet article de Vladimir Prus est également intéressant.
Mais pourquoi avons-nous besoin de la boucle while, ne pouvons-nous pas écrire:
if (!something_happened)
c.wait(m);
Nous ne pouvons pas. Et la raison meurtrière est que "attendre" peut revenir sans aucun appel 'notifier'. Cela s'appelle le réveil parasite et est explicitement autorisé par POSIX. Essentiellement, revenir de 'attendre' seulement indique que les données partagées peuvent ont changé, de sorte que les données doivent être évalué à nouveau.
D'accord, alors pourquoi cela n'est-il pas encore résolu? La première raison est que personne ne veut réparer. Appel enveloppant à 'attendre' dans une boucle est très souhaitée pour plusieurs autres raisons. Mais ces raisons nécessite une explication, bien que fausse le réveil est un marteau qui peut être appliqué à tout étudiant de première année sans échouer.
Autres conseils
Ce billet de blog donne une raison pour Linux, en termes de l'appel système futex
renvoyé lorsqu'un signal est transmis à un processus. Malheureusement, cela n'explique rien d'autre (et demande en effet plus d'informations).
La entrée de Wikipedia sur les fausses wakeups (qui semble être un concept à l'échelle de posix, btw, ne se limite pas à augmenter) peut également vous intéresser.