J'ai du mal à comprendre Wait()
, Pulse()
, PulseAll()
. Est-ce que tous éviteront l'impasse? J'apprécierais que vous expliquiez comment les utiliser?C#: Monitor - Wait, Pulse, PulseAll
Répondre
Version courte:
lock(obj) {...}
est sténographie pour Monitor.Enter
/Monitor.Exit
(avec gestion des exceptions, etc.). Si personne d'autre n'a le verrou, vous pouvez l'obtenir (et exécuter votre code) - sinon votre thread est bloqué jusqu'à ce que le verrou soit acquis (par un autre thread le relâchant).
Deadlock se produit généralement lorsque A: deux fils verrouiller les choses dans un ordre différent:
thread 1: lock(objA) { lock (objB) { ... } }
thread 2: lock(objB) { lock (objA) { ... } }
(ici, si chacun d'acquérir la première écluse, ne peut jamais obtenir le second, car aucun thread peut quitter pour libérer son verrou)
Ce scénario peut être réduit en verrouillant toujours dans le même ordre; et vous pouvez récupérer (dans une certaine mesure) en utilisant Monitor.TryEnter
(au lieu de Monitor.Enter
/lock
) et en spécifiant un délai d'expiration.
ou B: vous pouvez vous bloquer avec des choses comme WinForms lorsque le fil de commutation tout en maintenant un verrou:
lock(obj) { // on worker
this.Invoke((MethodInvoker) delegate { // switch to UI
lock(obj) { // oopsiee!
...
}
});
}
L'impasse semble évidente ci-dessus, mais il est pas évident quand vous avez du code spaghetti; réponses possibles: ne passez pas les threads en maintenant des verrous, ou utilisez BeginInvoke
pour pouvoir au moins quitter le verrou (laisser jouer l'interface utilisateur).
Wait
/Pulse
/PulseAll
sont différents; ils sont pour la signalisation.J'utilise cette in this answer pour signaler afin que:
Dequeue
: si vous essayez de données dequeue lorsque la file d'attente est vide, il attend un autre thread pour ajouter des données, qui se réveille le fil bloquéEnqueue
: si vous essayez de données enqueue lorsque la file d'attente est pleine, il attend un autre thread pour supprimer les données, ce qui réveille le thread bloqué
Pulse
ne se réveille jusqu'à un fil - mais je ne suis pas assez intelligent pour prouver que le prochain fil je s toujours celui que je veux, donc j'ai tendance à utiliser PulseAll
, et simplement re-vérifier les conditions avant de continuer; à titre d'exemple:
while (queue.Count >= maxSize)
{
Monitor.Wait(queue);
}
Avec cette approche, je peux en toute sécurité ajouter d'autres significations de Pulse
, sans mon code existant en supposant que « je me suis réveillé, donc il y a des données » - ce qui est pratique (dans le même exemple) Plus tard, j'ai dû ajouter une méthode Close()
.
Lecture Jon Skeet's multi-part threading article.
C'est vraiment bon. Ceux que vous mentionnez sont à peu près au tiers.
Non, ils ne vous protègent pas des blocages. Ce ne sont que des outils plus flexibles pour la synchronisation des threads. Voici une très bonne explication comment les utiliser et très important modèle d'utilisation - sans ce modèle, vous casserez toutes les choses: http://www.albahari.com/threading/part4.aspx
POUR NOVICES HAUTEMENT RECOMMANDER la page albahari est parfait! Parcourt le filetage et la synchronisation pas à pas avec des exemples clairs. – DanG
Ce sont des outils pour la synchronisation et la signalisation entre les threads. En tant que tels, ils ne font rien pour empêcher les blocages, mais s'ils sont utilisés correctement, ils peuvent être utilisés pour synchroniser et communiquer entre les threads.
Malheureusement, la plupart du travail nécessaire pour écrire du code multithread correct est actuellement la responsabilité des développeurs en C# (et de nombreuses autres langues). Jetez un oeil à la façon dont F #, Haskell et Clojure gère cela pour une approche entièrement différente.
Recette simple pour l'utilisation de Monitor.Wait et Monitor.Pulse. Il se compose d'un travailleur, un patron et un téléphone qu'ils utilisent pour communiquer:
object phone = new object();
Un fil "travailleur":
lock(phone) // Sort of "Turn the phone on while at work"
{
while(true)
{
Monitor.Wait(phone); // Wait for a signal from the boss
DoWork();
Monitor.PulseAll(phone); // Signal boss we are done
}
}
Un fil "Boss":
PrepareWork();
lock(phone) // Grab the phone when I have something ready for the worker
{
Monitor.PulseAll(phone); // Signal worker there is work to do
Monitor.Wait(phone); // Wait for the work to be done
}
Des exemples plus complexes suivent ...
Un "Travailleur avec autre chose à faire":
lock(phone)
{
while(true)
{
if(Monitor.Wait(phone,1000)) // Wait for one second at most
{
DoWork();
Monitor.PulseAll(phone); // Signal boss we are done
}
else
DoSomethingElse();
}
}
Un "Impatient Boss":
PrepareWork();
lock(phone)
{
Monitor.PulseAll(phone); // Signal worker there is work to do
if(Monitor.Wait(phone,1000)) // Wait for one second at most
Console.Writeline("Good work!");
}
Je ne comprends pas. Comment est-il possible que Boss et Worker soient en mode «téléphone» en même temps? – Marshall
@Marshall Monitor.Wait libère le verrou «téléphone» à la ligne suivante (vraisemblablement au patron). –
@DennisGorelik ahh, je vois. J'ai développé votre point [ci-dessous] (http://stackoverflow.com/a/42581381/1282864) – jdpilgrim
Malheureusement, aucune de Wait(), Pulse() ou PulseAll() ont la propriété magique qui vous Souhaiter - qui est que par en utilisant cette API vous éviterez automatiquement les interblocages.
Consultez le code suivant
object incomingMessages = new object(); //signal object
LoopOnMessages()
{
lock(incomingMessages)
{
Monitor.Wait(incomingMessages);
}
if (canGrabMessage()) handleMessage();
// loop
}
ReceiveMessagesAndSignalWaiters()
{
awaitMessages();
copyMessagesToReadyArea();
lock(incomingMessages) {
Monitor.PulseAll(incomingMessages); //or Monitor.Pulse
}
awaitReadyAreaHasFreeSpace();
}
Ce code impasse! Peut-être pas aujourd'hui, peut-être pas demain. Très probablement lorsque votre code est soumis à un stress parce que soudainement il est devenu populaire ou important, et vous êtes appelé à résoudre un problème urgent.
Pourquoi?
Finalement ce qui se passera:
- Tous les sujets de consommation font un travail
- Les messages arrivent, la zone prêt ne peut pas contenir plus de messages et PulseAll() est appelée.
- Aucun consommateur se réveiller, parce qu'aucun attendent
- Tous les sujets de consommation appel en attente() [BLOCAGE]
Cet exemple particulier suppose que le fil de producteur ne va jamais appeler PulseAll() à nouveau parce qu'il n'a pas plus d'espace pour mettre des messages. Mais il y a beaucoup, beaucoup de variations brisées sur ce code possible. Les gens vont essayer de le rendre plus robuste en changeant une ligne comme faisant Monitor.Wait();
dans
if (!canGrabMessage()) Monitor.Wait(incomingMessages);
Malheureusement, cela ne suffit pas encore pour le fixer. Pour résoudre ce problème, vous aussi besoin de changer la portée de verrouillage où Monitor.PulseAll()
est appelé:
LoopOnMessages()
{
lock(incomingMessages)
{
if (!canGrabMessage()) Monitor.Wait(incomingMessages);
}
if (canGrabMessage()) handleMessage();
// loop
}
ReceiveMessagesAndSignalWaiters()
{
awaitMessagesArrive();
lock(incomingMessages)
{
copyMessagesToReadyArea();
Monitor.PulseAll(incomingMessages); //or Monitor.Pulse
}
awaitReadyAreaHasFreeSpace();
}
Le point clé est que dans le code fixe, les verrous limitent les séquences d'événements:
Un fils de consommation fait son travail et boucles
Ce fil acquiert le verrou
Et grâce au verrouillage, il est maintenant vrai que soit:
a. Messages ont pas encore est arrivé dans la zone de préparation, et il libère le verrou en appelant Wait() avant que le fil du récepteur de message peut acquérir le verrou et copier plus de messages dans la zone de préparation, ou
b. Les messages sont déjà arrivés dans la zone prête et il reçoit les messages au lieu d'appeler Wait(). (Et pendant qu'il prend cette décision, il est impossible pour le fil de récepteur de message par exemple acquérir le verrou et copier plus de messages dans la zone de préparation.)
En conséquence, le problème du code d'origine maintenant ne se produit jamais : 3. Lorsque PulseEvent() est appelée Aucun consommateur se réveiller, parce qu'aucun attendent
maintenant observer que, dans ce code, vous devez obtenir le champ de verrouillage droit exactement. (Si, en effet, je l'ai eu droit!)
Et aussi, puisque vous devez utiliser le lock
(ou Monitor.Enter()
etc.) afin d'utiliser Monitor.PulseAll()
ou Monitor.Wait()
d'une manière sans blocage, vous avez encore à vous soucier de la possibilité de autres les blocages qui se produisent à cause de ce blocage.
Bottom line: ces API sont également faciles à vis et une impasse avec, par exemple tout à fait dangereux
Quelque chose que le total m'a jeté ici est que Pulse
donne juste un « heads up » à un fil dans un Wait
.Le thread Attente ne continuera pas jusqu'à ce que le thread qui a fait le Pulse
abandonne le verrou et le thread en attente l'emporte avec succès.
lock(phone) // Grab the phone
{
Monitor.PulseAll(phone); // Signal worker
Monitor.Wait(phone); // ****** The lock on phone has been given up! ******
}
ou
lock(phone) // Grab the phone when I have something ready for the worker
{
Monitor.PulseAll(phone); // Signal worker there is work to do
DoMoreWork();
} // ****** The lock on phone has been given up! ******
Dans les deux cas, il n'est pas jusqu'à ce que « le verrou sur le téléphone a été abandonné » qu'un autre thread peut obtenir.
Il peut y avoir d'autres threads en attente de ce verrou de Monitor.Wait(phone)
ou lock(phone)
. Seul celui qui remportera le verrou pourra continuer.
- 1. internes de "Wait", "Pulse", "PulseAll"
- 2. applications Monitor
- 3. Condition Monitor.Wait/Pulse dans un serveur multithread
- 4. Monitor IIS 6.0
- 5. Windows Temperature Monitor
- 6. Java - wait et notifyAll
- 7. WAIT in Transaction - Firebird
- 8. DTE.ExecuteCommand et wait
- 9. classmethod pour Tkinter-Monitor-Window
- 10. Toute documentation pour l'API 'Monitor' de Nvidia?
- 11. Comment me connecter à l'API Campaign Monitor?
- 12. jQuery - Wait jusqu'à la fin de SlideUp()
- 13. Wait active dans Windows I/O pilote
- 14. Utilisation correcte de fork, wait, exit, etc
- 15. WPF Wait Cursor avec BackgroundWorker Thread
- 16. mysql lock wait timeout dépassé, essayez de redémarrer la transaction
- 17. Comment utiliser Process Monitor pour résoudre un UnauthorizedAccessException
- 18. Implémentation de Monitor avec signalisation à l'aide de mutex et variable de condition en C++
- 19. Comment afficher les utilisateurs actuels avec Performance Monitor?
- 20. Pouvez-vous exécuter Jython sur la machine virtuelle Java qui s'exécute sur les stylets intelligents Pulse?
- 21. La différence entre Monitor.Pulse et Monitor.PulseAll
- 22. Existe-t-il un moyen de développer le volet Processus dans Sql Server 2008 Activity Monitor?
- 23. Comment puis-je savoir pourquoi subprocess.Popen wait() attend toujours si stdout = PIPE?
- 24. Est-ce que App WAIT for -initWithContentsOfURL: pour récupérer des données Web?
- 25. Comment savoir quel processus utilise « wait CPU » (à savoir I/O bloqué)
- 26. Silverlight ReaderWriterLock Mise en œuvre Bonne/Mauvaise?
- 27. Est-ce un bogue dans l'instruction .net Monitor/lock ou est-ce que MessageBox.Show se comporte différemment?
- 28. comment créer un msg "Uploading! Pls wait ..." pour télécharger le fichier Excel et le charger sur SQL Server?
- 29. C# travailler avec singleton à partir de deux threads différents
- 30. attente instruction dans le composant C?
il ne mentionne même pas Pulse ou attendre! Lien vers un article de John Skeet ne fait pas automatiquement une bonne réponse ... –
Oh vraiment? Qu'est-ce que c'est alors? 'http: // www.yoda.arachsys.com/csharp/threads/deadlocks.shtml' – Geo
@Geo: oui, celui-ci s'adapte mieux;) –