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
:
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();