Pourquoi la plupart des implémentations de mutex sont-elles injustes?


11

Ma compréhension est que les implémentations les plus populaires d'un mutex (par exemple std :: mutex en C ++) ne garantissent pas l' équité - c'est-à-dire qu'elles ne garantissent pas qu'en cas de conflit, le verrou sera acquis par les threads dans l'ordre où ils appelé lock (). En fait, il est même possible (bien que, espérons-le, peu fréquent) qu'en cas de conflit élevé, certains des threads en attente d'acquérir le mutex ne puissent jamais l' acquérir.

Cela me semble un comportement inutile - il me semble qu'un mutex équitable produirait un comportement plus conforme à ce qu'un programmeur voudrait / attendrait.

La raison invoquée pour expliquer pourquoi les mutex ne sont généralement pas mis en œuvre pour être équitables est la «performance», mais j'aimerais mieux comprendre ce que cela signifie - en particulier, comment le relâchement de l'exigence d'équité du mutex améliore-t-il les performances? Il semble qu'un mutex «juste» serait trivial à implémenter - il suffit que lock () ajoute le thread appelant à la fin de la liste liée du mutex avant de mettre le thread en veille, puis de déverrouiller () pop le thread suivant à partir de la tête de cette même liste et le réveiller.

Quel aperçu de la mise en œuvre du mutex me manque ici, qui expliquerait pourquoi il a été jugé utile de sacrifier l'équité pour de meilleures performances?


1
Cette liste chaînée pour chaque mutex devrait être une structure de données partagée, non? Alors, comment allez-vous empêcher les courses de données à ce sujet sans diminuer les performances?
user3543290

À l'aide d'un mécanisme de liste chaînée sans verrou, je pense. Quelle structure de données un mutex injuste utilise-t-il pour trouver le prochain thread à réveiller?
Jeremy Friesner

1
Vous devrez vérifier cela, mais une liste chaînée sans verrou garantit-elle l'équité? Je pense que vous constaterez que des garanties telles que l'équité dans la programmation simultanée sont difficiles à trouver.
user3543290

Réponses:


7

La réponse de Jim Sawyer indique une réponse: lorsque vous avez des fils avec des priorités différentes, un comportement "juste" serait incorrect. Lorsque plusieurs threads peuvent s'exécuter, le thread de priorité la plus élevée est généralement celui qui doit s'exécuter.

Cependant, il y a un secret peu discuté de l'implémentation du système d'exploitation que vous devez savoir, à savoir que les systèmes d'exploitation exécutent parfois du code en tant qu'utilisateur en détournant un thread utilisateur. Pour des raisons de sécurité, la plupart des systèmes d'exploitation qui le font ne le font que lorsqu'un thread est bloqué. Lorsque le système d'exploitation est terminé, le thread est à nouveau suspendu, ce qui a généralement pour effet de déplacer le thread vers l'arrière de la file d'attente.

Un exemple typique est un gestionnaire de signaux sous Unix, une interruption système asynchrone dans VMS ou un appel de procédure asynchrone dans Windows NT. Ce sont essentiellement la même chose: le système d'exploitation doit informer le processus utilisateur qu'un événement s'est produit, et cela est géré en exécutant du code dans l'espace utilisateur.

De nombreux services de système d'exploitation tels que les E / S asynchrones sont souvent implémentés au-dessus de cette fonction.

Un autre exemple est si le processus est sous le contrôle d'un débogueur. Dans ce cas, le système de débogage peut exécuter du code en tant que tâche utilisateur pour diverses raisons.


4

«L'inversion prioritaire» est l'une des raisons pour lesquelles l'équité peut être indésirable. Un processus de faible priorité frappe le mutex verrouillé et s'endort. Ensuite, un processus de priorité plus élevée le frappe et dort également. Lorsque le mutex se déverrouille, quel processus devrait obtenir le verrou ensuite?


Bienvenue sur le site et merci d'avoir répondu à une question qui traîne depuis un moment sans réponse! Votre réponse est, bien sûr, correcte, mais je pense qu'elle pourrait contenir un peu plus de détails.
David Richerby

3

Un mutex juste passera plus de sa vie enfermé qu'un mutex injuste, toutes choses étant égales par ailleurs. Parce qu'un thread libérant un mutex injuste peut toujours le déverrouiller. Mais un thread libérant un mutex équitable ne peut le déverrouiller que lorsque la file d'attente du serveur est vide. Sinon, le thread de libération doit laisser le mutex verrouillé pour le prochain thread, alias le premier thread de la file d'attente du serveur, qui est ensuite retiré de la file d'attente et réveillé. Le mutex reste verrouillé au moins jusqu'à ce que le thread nouvellement réveillé soit planifié sur un CPU, ce qui peut être long s'il y a de nombreux threads exécutables.

Et si le thread de libération essaie rapidement de réacquérir le même mutex, il doit se placer à l'arrière de la file d'attente du serveur et se mettre en veille. Cela ne serait pas arrivé si le thread n'avait pas libéré le mutex pour commencer. Par conséquent, cela incite les sections critiques «plus gourmandes» plus longues.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.