12
votes

Moniteur.Wait, variable de condition

Étant donné un extrait de code suivant (trouvé dans quelque part tout en apprenant le filetage).

 if(queue.Count == 0)
       Monitor.Wait(sync);


3 commentaires

Vous voudrez peut-être spécifier la langue et ajouter une balise appropriée.


J'ai oublié que quelqu'un a déjà ajouté c # tag.


C'est à voir avec la sémantique Hoare contre Sémantique Mesa [Schedulting Mesa]. En général, vous devez utiliser des boucles avec la planification de MESA. Mais dans votre cas particulier, cela n'aurait pas d'importance.


5 Réponses :


3
votes

Vous devez vérifier si la file d'attente est toujours vide ou non. En utilisant uniquement si vous ne le vérifieriez qu'une seule fois, attendez un moment, puis une déléue. Et si à ce moment-là, la file d'attente est toujours vide? CLAQUER! Erreur de sous-ensemise de la file d'attente ...


1 commentaires

Mais une attente ne se réveille que après une impulsion, et cela arrive quand quelque chose est en cours.



1
votes

avec si condition quand quelque chose a libéré le verrouillage de la queue 0 ne vérifiera pas à nouveau et peut-être une erreur de sousfulture de la file d'attente afin que nous devons vérifier la condition chaque temps à cause de la concurrence et cela s'appelle Spinning


0 commentaires

-1
votes
if (queue.Count == 0)  
will do.Using while loop pattern for "wait for and check condition" context is a legacy leftover, I think. Because non-Windows, non-.NET monitor variables can be triggered without actual Pulse.In .NET, you private monitor variable cannot be triggered without Queue filling so you don't need to worry about queue underflow after monitor waiting. But, it is really not bad habit to use while loop for "wait for and check condition".

5 commentaires

Cela me semble très complexe, pouvez-vous indiquer une ressource sur laquelle je peux comprendre ce que vous voulez dire.


Eh bien .. j'ai bien peur de ne pas pouvoir fournir une ressource plus détaillée à ce sujet. Cela vient de ma propre expérience. J'ai utilisé 'si ..' coding de nombreuses années sous Windows et n'avait aucun problème. Quand j'ai utilisé 'si ..' coding à Linux? tous foirés. Vous devez utiliser la boucle pour les variables de moniteur (condition) dans la plate-forme * Nix. Après cela, j'utilise toujours tout en boucle quelle que soit la langue / la plate-forme.


IMHO Un simple si ne fera pas. Vous pouvez rencontrer une condition de course si un autre thread délesille entre-temps. Je soupçonne que cela dépend si pulseall ou juste impulsion est appelé ...


Même si impulsion a été utilisé, il y aurait toujours un problème avec le simple si chèque.


Les threads multiples ne peuvent pas exécuter «si ...» du code en raison de la déclaration de verrouillage précédent (Sync).



18
votes

Vous devez comprendre ce que pulse , pulseall et attendre fait. Le moniteur maintient deux files d'attente: la file d'attente d'attente et la file d'attente Ready. Lorsqu'un fil d'appels attend il est déplacé dans la file d'attente d'attente. Lorsqu'un fil appelle impulsion Il déplace un et un seul fil de la file d'attente en attente à la file d'attente Ready. Lorsqu'un filetage appelle pulseall il déplace tous les discussions de la file d'attente en attente à la file d'attente Ready. Les fils dans la file d'attente Ready sont éligibles pour réacquérir la serrure à tout moment, mais seulement après la libération du détenteur actuel.

Sur la base de ces connaissances, il est assez facile de comprendre pourquoi vous devez revérifier le décompte de la file d'attente lorsque vous utilisez pulseall . C'est parce que tous de threads de déquenseur sera éventuellement réveillez-vous et voudra essayer d'extraire un élément de la file d'attente. Mais, si s'il n'y a qu'un seul article dans la file d'attente pour commencer? Évidemment, nous devons revérifier le nombre de file d'attente pour éviter de déroger une file d'attente vide.

Alors, quelle serait la conclusion si vous aviez utilisé pulse au lieu de pulseall ? Il y aurait toujours un problème avec le simple si chèque. La raison en est que le thread de la file d'attente Ready ne sera pas nécessairement le prochain thread pour acquérir la serrure. C'est-à-dire que le moniteur ne donne pas de préférence à un attendre appel au-dessus d'un entrée appel.

Le pendant est un motif assez standard lors de l'utilisation de moniteur.Wait . C'est parce que pulsant un fil n'a pas de sens sémantique par lui-même. Ce n'est qu'un signal que l'état de verrouillage a changé. Lorsque les threads se réveillent après avoir bloqué sur attendre ils doivent revendre la même condition utilisée à l'origine pour bloquer le thread pour voir si le thread peut maintenant procéder. Parfois, il ne peut pas et il devrait donc bloquer un peu plus.

La meilleure règle de base ici est que s'il y a un doute sur l'utilisation d'un si vérifier ou un lors de la vérification alors choisissez toujours un pendant boucle parce qu'il est plus sûr. En fait, je prendrais cela à l'extrême et suggérez à toujours utiliser un pendant que car il n'y a pas d'avantage inhérent à l'utilisation du plus simple si Vérifier et parce que le contrôle si est presque toujours le mauvais choix de toute façon. Une règle similaire contient pour choisir s'il faut utiliser impulsion ou pulseall . S'il y a un doute sur lequel utiliser alors toujours choisir pulseall .


4 commentaires

+1 Guide très utile sur l'utilisation du moniteur. En effet, «si» sans double verrouillage n'est rien d'autre qu'un désastre de débogage.


@Brain Gideon. Explication géniale!


Ne pensez-vous pas que le moniteur .NET est imparfait dans la conception? Il ne devrait pas y avoir que deux files d'attente; Il devrait y avoir plus d'une file d'attente en attente, chacune pour une condition d'attente particulière spécifiée par la paire de la variable , comme ce que fait Pthreads. Avoir une grande file d'attente en attente conduit à des commutateurs de contexte excessifs lorsqu'un thread ne se réveille que la recherche de la condition d'attente n'a pas changé du tout, puis revenez immédiatement à la file d'attente.


@ H9UEST: Vous faites un bon point. Je ne sais pas si j'irais aussi loin que de dire que c'est un défaut, mais il y a définitivement des inconvénients avec le design actuel. Pourrait-il être meilleur? Probablement ... oui, mais à quel coût?



0
votes

Pourquoi sur UNIX, cela pourrait aller mal, c'est due au réveil parasite, possibilité causée par des signaux de système d'exploitation. C'est un effet secondaire qui n'est pas garanti de ne jamais arriver sous Windows également. Ce n'est pas un héritage, c'est la façon dont OS fonctionne. Si les moniteurs sont mis en œuvre en termes de variable de condition, c'est-à-dire.

DEF: A Eassious Wake Up est une nouvelle planification d'un fil de couchage sur un site d'attente variable de condition, qui n'a pas été déclenché par une action provenant des threads de programme actuels (comme PULSE () ).

Ce désagrément pourrait être masqué dans des langues gérées par exemple. les files d'attente. Donc, avant de sortir de la fonction wait () , le cadre peut vérifier que ce thread en cours d'exécution est réellement demandé pour la planification, si cela ne se trouve pas dans une file d'attente d'exécution, elle peut revenir au sommeil. . Cacher le problème.


0 commentaires