Il n'y a donc pas de raison technique ?
J'ai upvoted la réponse de cmeerw parce que je crois qu'il a donné une raison technique. Faisons le tour de la question. Imaginons que la commission ait décidé d'avoir condition_variable
attendre sur un mutex
. Voici un code utilisant ce modèle :
void foo()
{
mut.lock();
// mut locked by this thread here
while (not_ready)
cv.wait(mut);
// mut locked by this thread here
mut.unlock();
}
C'est exactement comme ça qu'on ne devrait pas utiliser un condition_variable
. Dans les régions marquées par :
// mut locked by this thread here
il y a un problème de sécurité exceptionnel, et c'est un problème sérieux. Si une exception est lancée dans ces zones (ou par cv.wait
), l'état verrouillé du mutex est divulgué à moins qu'un try/catch ne soit également placé quelque part pour attraper l'exception et le déverrouiller. Mais ce n'est que du code supplémentaire que vous demandez au programmeur d'écrire.
Disons que le programmeur sait comment écrire du code sécurisé par les exceptions, et sait utiliser unique_lock
pour y parvenir. Maintenant, le code ressemble à ceci :
void foo()
{
unique_lock<mutex> lk(mut);
// mut locked by this thread here
while (not_ready)
cv.wait(*lk.mutex());
// mut locked by this thread here
}
C'est beaucoup mieux, mais ce n'est toujours pas une situation idéale. Le site condition_variable
oblige le programmeur à faire des pieds et des mains pour que les choses fonctionnent. Il y a un déréférencement possible du pointeur nul si lk
ne fait pas accidentellement référence à un mutex. Et il n'y a aucun moyen pour condition_variable::wait
pour vérifier que ce fil possède bien le verrou sur mut
.
Oh, je viens de me rappeler qu'il y a aussi le risque que le programmeur choisisse le mauvais unique_lock
pour exposer le mutex. *lk.release()
serait désastreux ici.
Maintenant, regardons comment le code est écrit avec l'actuel condition_variable
qui prend un unique_lock<mutex>
:
void foo()
{
unique_lock<mutex> lk(mut);
// mut locked by this thread here
while (not_ready)
cv.wait(lk);
// mut locked by this thread here
}
- Ce code est aussi simple qu'il peut l'être.
- Il s'agit d'une sécurité exceptionnelle.
- Le site
wait
peut vérifier lk.owns_lock()
et lancer une exception si c'est le cas false
.
Ce sont des raisons techniques qui ont motivé la conception de l'API de l'UE. condition_variable
.
En outre, condition_variable::wait
ne prend pas de lock_guard<mutex>
parce que lock_guard<mutex>
c'est comment on dit : Je possède le verrou sur ce mutex jusqu'à ce que lock_guard<mutex>
détruit. Mais lorsque vous appelez condition_variable::wait
vous libérez implicitement le verrou sur le mutex. Cette action est donc incompatible avec la lock_guard
cas d'utilisation / déclaration.
Nous avions besoin unique_lock
de toute façon, afin de pouvoir renvoyer des verrous à partir de fonctions, les placer dans des conteneurs et verrouiller/déverrouiller des mutex dans des schémas non scopés de manière sûre du point de vue des exceptions. unique_lock
était le choix naturel pour condition_variable::wait
.
Mise à jour
bamboon a suggéré dans les commentaires ci-dessous que je mette en contraste condition_variable_any
alors voilà :
Question : Pourquoi n'est-ce pas condition_variable::wait
afin que je puisse passer n'importe quel Lockable
type à elle ?
Réponse :
C'est une fonctionnalité vraiment cool à avoir. Par exemple cet article démontre un code qui attend sur un shared_lock
(rwlock) en mode partagé sur une variable de condition (quelque chose d'inédit dans le monde posix, mais néanmoins très utile). Cependant, cette fonctionnalité est plus coûteuse.
La commission a donc introduit un nouveau type avec cette fonctionnalité :
`condition_variable_any`
Avec cette condition_variable
adaptateur on peut attendre tout type verrouillable. S'il a des membres lock()
y unlock()
vous êtes prêt à partir. Une mise en œuvre correcte de condition_variable_any
nécessite un condition_variable
et un élément de données shared_ptr<mutex>
membre des données.
Parce que cette nouvelle fonctionnalité est plus onéreuse que votre service de base condition_variable::wait
et parce que condition_variable
est un outil de si bas niveau, cette fonctionnalité très utile mais plus coûteuse a été placée dans une classe séparée afin que vous ne payiez que si vous l'utilisez.