Thread.sleep vs Monitor.Wait vs RegisteredWaitHandle?

(Les éléments suivants ont des objectives différents, mais je suis intéressant de savoir comment ils ont “PAUSE”)

des questions

Thread.sleep – Est-ce que cela Thread.sleep les performances sur un système? Est-ce qu’il Thread.sleep un thread à son attente?

Monitor.Wait il de Monitor.Wait ? Quelle est la différence dans la façon dont ils “attendent”? est-ce qu’ils attachent un fil avec leur attente?

Qu’en est-il de RegisteredWaitHandle ? Cette méthode accepte un délégué qui est exécuté lorsqu’un descripteur d’attente est signalé. Pendant qu’il attend, cela ne bloque pas un fil.

certains fils sont donc en pause et peuvent être réveillés par un délégué, alors que d’autres attendent juste? tourner ?

quelqu’un peut-il s’il vous plaît rendre les choses plus claires?

modifier

http://www.albahari.com/threading/part2.aspx

entrez la description de l'image ici

Thread.Sleep et Monitor.Wait placent le thread dans l’état WaitSleepJoin :

WaitSleepJoin: le thread est bloqué. Cela peut être dû à l’appel de Thread :: Sleep ou Thread :: Join, à la demande d’un verrou – par exemple, en appelant Monitor :: Enter ou Monitor :: Wait – ou à l’attente d’un object de synchronisation de thread tel que ManualResetEvent.

RegisteredWaitHandle s’obtient en appelant RegisterWaitForSingleObject et en passant un WaitHandle . En général, tous les descendants de cette classe utilisent des mécanismes de blocage. L’appel en Wait place à nouveau le thread dans WaitSleepJoin (par exemple, AutoResetEvent ).

Voici une autre citation de MSDN:

La méthode RegisterWaitForSingleObject vérifie l’état actuel du WaitHandle de l’object spécifié. Si l’état de l’object n’est pas signalé, la méthode enregistre une opération d’attente. L’opération d’attente est effectuée par un thread du pool de threads. Le délégué est exécuté par un thread de travail lorsque l’état de l’object est signalé ou que le délai d’attente s’est écoulé.

Donc, un thread dans le pool attend le signal.

En ThreadPool.RegisterWaitForSingleObject qui concerne ThreadPool.RegisterWaitForSingleObject , cela ne ThreadPool.RegisterWaitForSingleObject pas un thread par enregistrement (en pool ou autrement). Vous pouvez le tester facilement: exécutez le script suivant dans LINQPad qui appelle cette méthode 20 000 fois:

 static ManualResetEvent _starter = new ManualResetEvent (false); void Main() { var regs = Enumerable.Range (0, 20000) .Select (_ => ThreadPool.RegisterWaitForSingleObject (_starter, Go, "Some Data", -1, true)) .ToArray(); Thread.Sleep (5000); Console.WriteLine ("Signaling worker..."); _starter.Set(); Console.ReadLine(); foreach (var reg in regs) reg.Unregister (_starter); } public static void Go (object data, bool timedOut) { Console.WriteLine ("Started - " + data); // Perform task... } 

Si ce code bloquait 20 000 threads pendant la durée de “l’attente” de 5 secondes, il ne pourrait probablement pas fonctionner.

Modifier – en réponse à:

“C’est une preuve. Mais existe-t-il encore un seul thread qui vérifie les signaux uniquement? dans le pool de threads?”

Ceci est un détail de mise en œuvre. Oui, il peut être implémenté avec un seul thread qui décharge les rappels vers le pool de threads gérés, bien que cela ne soit pas garanti. Les poignées d’attente sont finalement gérées par le système d’exploitation, ce qui déclenchera probablement les rappels également. Il peut utiliser un thread (ou un petit nombre de threads) dans son implémentation interne. Ou avec des interruptions, il ne peut pas bloquer un seul thread. Cela peut même varier en fonction de la version du système d’exploitation. Il s’agit d’un détail de mise en œuvre qui ne nous concerne pas vraiment.

S’il est vrai, RegisterWaitForSingleObject crée des threads d’attente, mais chaque appel n’en crée pas un.

De MSDN :

De nouveaux threads d’attente sont créés automatiquement lorsque requirejs

Du blog de Raymond Chen:

… au lieu de coûter un fil entier, il en coûte quelque chose de plus proche (mais pas exactement) de 1/64 d’un fil

Donc, utiliser RegisterWaitForSingleObject est généralement préférable à la création de vos propres threads d’attente.

Thread.Sleep et RegisteredWaitHandle fonctionnent à différents niveaux. Laissez-moi essayer et éclaircir:

Les processus ont plusieurs threads, qui s’exécutent simultanément (en fonction du planificateur de système d’exploitation). Si un thread appelle Thread.Sleep ou Monitor.Wait , il ne tourne pas – il est mis à l’état WaitSleepJoin et le processeur est atsortingbué aux autres threads.

Maintenant, lorsque vous avez plusieurs éléments de travail simultanés, vous utilisez un pool de threads, mécanisme qui crée plusieurs threads et utilise sa propre compréhension des éléments de travail pour répartir les appels sur ses threads. Dans ces modèles, les threads de travail sont appelés à partir du répartiteur de pools de threads pour effectuer certains travaux, puis reviennent dans le pool. Si un thread de travail appelle une opération de blocage, telle que Thread.Sleep ou Monitor.Wait , ce thread est “lié”, car le répartiteur de pool de threads ne peut pas l’utiliser pour des éléments de travail supplémentaires.

Je ne connais pas bien l’API, mais je pense que RegisteredWaitHandle demanderait au répartiteur de pool de threads d’appeler un thread de travail en cas de besoin – et que votre propre thread n’est pas “lié” et peut poursuivre son travail ou revenir au pool de threads. .

ThreadPool.g RegisterWaitForSingleObject appelle dans son implémentation native finalement QueueUserAPC . Voir les sources du rotor (sscli20 \ clr \ src \ vm \ win32threadpool.cpp (1981)). Contrairement à Wait Thread.Sleep, votre thread ne sera pas interrompu par l’utilisation de RegisterWaitForSingleObject.

À la place, pour ce thread, une queue FIFO avec des rappels en mode utilisateur est enregistrée. Elle est appelée lorsque le thread est dans un état pouvant être alerté. Cela signifie que vous pouvez continuer à travailler et lorsque votre thread est bloqué, le système d’exploitation fonctionne sur les rappels enregistrés, ce qui permet à votre thread de faire quelque chose de significatif en attendant.

Edit1:

Pour compléter l’parsing. Sur le thread qui a appelé RegisterWaitForSingleObject, un rappel est appelé sur le thread lorsqu’il se trouve dans un état alertable. Une fois que cela se produit, le thread qui a appelé RegisterWaitForSingleObject exécute un rappel CLR qui enregistre un autre rappel traité par un thread d’attente de rappel de pool de threads qui attend uniquement les rappels signalés. Ce thread en attente de rappel du pool de threads va ensuite vérifier à intervalles réguliers les rappels signalés.

Ce thread d’attente appelle enfin QueueUserWorkItem pour que le rappel signalé soit exécuté sur un thread de pool de threads.