Différence entre TPL & async / waiting (Manipulation des threads)

Essayer de comprendre la différence entre le TPL et l’ async / await en matière de création de threads.

Je crois que le TPL ( TaskFactory.StartNew ) fonctionne de manière similaire à ThreadPool.QueueUserWorkItem en ce sens qu’il met en queue le travail sur un thread dans le pool de threads. C’est bien sûr sauf si vous utilisez TaskCreationOptions.LongRunning qui crée un nouveau thread.

Je pensais async / await fonctionnerait de manière similaire, essentiellement:

TPL:

 Factory.StartNew( () => DoSomeAsyncWork() ) .ContinueWith( (antecedent) => { DoSomeWorkAfter(); },TaskScheduler.FromCurrentSynchronizationContext()); 

Async / Await :

 await DoSomeAsyncWork(); DoSomeWorkAfter(); 

serait identique. D’après ce que j’ai lu, il semble que l’ async / await only “Parfois” crée un nouveau thread. Alors, quand crée-t-il un nouveau thread et quand ne crée-t-il pas un nouveau thread? Si vous avez affaire à des ports d’achèvement IO, je peux voir qu’il n’a pas à créer de nouveau thread, mais je pense que c’est le cas. Je suppose que ma compréhension de FromCurrentSynchronizationContext était toujours un peu floue. J’ai toujours traversé le fil de l’interface utilisateur.

Je crois que la TPL (TaskFactory.Startnew) fonctionne de manière similaire à ThreadPool.QueueUserWorkItem en ce sens qu’elle met en queue le travail sur un thread dans le pool de threads.

À peu près .

De ce que j’ai lu, il semble que async / wait only “parfois” crée un nouveau thread.

En fait, ça ne le fait jamais. Si vous voulez du multithreading, vous devez l’implémenter vous-même. Il existe une nouvelle méthode Task.Run qui est juste un raccourci pour Task.Factory.StartNew , et c’est probablement le moyen le plus courant de démarrer une tâche sur le pool de threads.

Si vous avez affaire à des ports d’achèvement IO, je peux voir qu’il n’a pas à créer de nouveau thread, mais je pense que c’est le cas.

Bingo Ainsi, des méthodes telles que Stream.ReadAsync vont créer un wrapper de Task autour d’un IOCP (si le Stream possède un IOCP).

Vous pouvez également créer des “tâches” non-I / O, non-CPU. Un exemple simple est Task.Delay , qui renvoie une tâche qui se termine après un certain temps.

Ce qui est intéressant avec l’ async / await c’est que vous pouvez mettre du travail dans le pool de threads (par exemple, Task.Run ), effectuer des opérations liées aux E / S (par exemple, Stream.ReadAsync ) et effectuer d’autres opérations (par exemple, Task.Delay ) … et ils sont tous des tâches! Ils peuvent être attendus ou utilisés dans des combinaisons comme Task.WhenAll .

Toute méthode qui renvoie une Task peut être await – elle ne doit pas nécessairement être une méthode async . Ainsi, les opérations Task.Delay et les opérations liées aux E / S utilisent simplement TaskCompletionSource pour créer et TaskCompletionSource une tâche – la seule chose à faire sur le pool de threads est l’achèvement de la tâche lorsque l’événement se produit (expiration, E / S, etc.).

Je suppose que ma compréhension de FromCurrentSynchronizationContext était toujours un peu floue. J’ai toujours traversé le fil de l’interface utilisateur.

J’ai écrit un article sur SynchronizationContext . La plupart du temps, SynchronizationContext.Current :

  • est un contexte d’interface utilisateur si le thread en cours est un thread d’interface utilisateur.
  • est un contexte de requête ASP.NET si le thread en cours traite une requête ASP.NET.
  • est un contexte de pool de threads autrement.

Tout thread peut définir son propre SynchronizationContext , il existe donc des exceptions aux règles ci-dessus.

Notez que l’atsortingbut de Task par défaut planifiera le rest de la méthode async sur le SynchronizationContext actuel s’il n’est pas nul . sinon, il va sur le TaskScheduler actuel. Ce n’est pas si important aujourd’hui, mais dans un avenir proche, ce sera une distinction importante.

J’ai écrit ma propre intro async / wait sur mon blog, et Stephen Toub a récemment publié une excellente FAQ async / en await .

En ce qui concerne “simultanéité” vs “multithreading”, voir cette question SO associée . Je dirais async active la concurrence, qui peut ou non être multithread. Il est facile à utiliser await Task.WhenAll ou await Task.WhenAny effectue un traitement simultané, et à moins que vous n’utilisiez explicitement le pool de threads (par exemple, Task.Run ou ConfigureAwait(false) ), plusieurs opérations simultanées sont en cours au en même temps (par exemple, plusieurs E / S ou d’autres types tels que le Delay ) – et il n’y a pas de thread nécessaire pour eux. J’utilise le terme “simultanéité mono-thread” pour ce type de scénario, même si dans un hôte ASP.NET, vous pouvez en réalité obtenir une “concurrence simultanée”. Ce qui est plutôt gentil.

async / waiting simplifie fondamentalement les méthodes ContinueWith (Continuations dans le style de passage ContinueWith )

Il n’introduit pas la concurrence – vous devez toujours le faire vous-même (ou utiliser la version Async d’une méthode de framework.)

Donc, la version C # 5 serait:

 await Task.Run( () => DoSomeAsyncWork() ); DoSomeWorkAfter();