La différence entre le TPL & async/await (la manipulation du Fil)
Essayer de comprendre la différence entre le TPL & async
/await
quand il s'agit de la création de threads.
Je crois que le TPL (TaskFactory.StartNew
) fonctionne de manière similaire à ThreadPool.QueueUserWorkItem
en ce qu'il met en file d'attente de 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.
J'ai pensé async
/await
fonctionnerait de la même façon donc, essentiellement:
TPL:
Factory.StartNew( () => DoSomeAsyncWork() )
.ContinueWith(
(antecedent) => {
DoSomeWorkAfter();
},TaskScheduler.FromCurrentSynchronizationContext());
Async
/Await
:
await DoSomeAsyncWork();
DoSomeWorkAfter();
serait identique. De ce que j'ai lu, il semble que async
/await
seulement "parfois" crée un nouveau thread. Alors quand est-il de créer un nouveau thread et quand n'est-il pas de créer un nouveau thread? Si nous avions affaire à IO ports de fin je le vois pas avoir à créer un nouveau fil de discussion, mais sinon je pense qu'il faudrait. Je suppose que ma compréhension de FromCurrentSynchronizationContext
toujours était un peu floue aussi. J'ai toujours pensé que c'était, en substance, le thread de l'INTERFACE utilisateur.
- En Fait, TaskCreationOptions.LongRunning ne garantit pas un "nouveau fil". Par MSDN, le "LongRunning" option ne fournit qu'un indice pour le planificateur; il ne garantit pas un thread dédié. J'ai trouvé que la manière dure.
- bien que ce que vous dites à propos de la documentation est correcte, j'ai regardé le TPL du code source d'un moment de retour et je suis assez sûr que, en fait, un nouveau thread dédié est toujours créé lors de
TaskCreationOptions.LongRunning
est spécifié. - Vous voudrez peut-être prendre un autre regard. Je sais que c'était la mise en commun des discussions
Thread.CurrentThread.IsThreadPoolThread
était de retourner true pour court-threads en cours d'exécution de quelques centaines de millisecondes. sans parler de la ThreadStatic variables j'ai été en utilisant des saignements dans plusieurs threads, ce qui entraîne toutes sortes de havok. J'ai eu la force de mon code à nouveau plusieurs Thread()s, old school, afin de garantir un thread dédié. En d'autres termes, je ne pouvais pas utiliser le TaskFactory pour les threads dédiés. Eventuellement, vous pouvez mettre en place votre propreTaskScheduler
qui renvoie toujours un thread dédié.
Vous devez vous connecter pour publier un commentaire.
Assez bien.
En fait, il ne le fait jamais. Si vous voulez le multithreading, vous avez à mettre en œuvre vous-même. Il y a une nouvelle
Task.Run
méthode qui est juste un raccourci pourTask.Factory.StartNew
, et c'est probablement la façon la plus courante de commencer une tâche sur le pool de threads.De Bingo. Ainsi, les méthodes comme
Stream.ReadAsync
va réellement créer unTask
wrapper autour d'un IOCP (si leStream
a un IOCP).Vous pouvez également créer des non-I/O, la non-UC "tâches". Un exemple simple est
Task.Delay
, qui renvoie une tâche qui se déroule après une certaine période de temps.La chose cool à propos de
async
/await
est que vous pouvez faire la queue un peu de travail pour le pool de threads (par exemple,Task.Run
), effectuer des e/S de l'opération (par exemple,Stream.ReadAsync
), et faire une autre opération (par exemple,Task.Delay
)... et elles sont toutes les tâches! Ils peuvent être attendu ou utilisés dans des combinaisons commeTask.WhenAll
.Toute méthode qui retourne
Task
peut êtreawait
ed - il n'a pas à être unasync
méthode. DoncTask.Delay
et I/O-les opérations liées suffit d'utiliserTaskCompletionSource
de créer et de remplir une tâche - la seule chose qui est fait sur le pool de threads est l'achèvement de la tâche lorsque l'événement se produit (délai d'expiration, la fin d'e/S, etc).J'ai écrit un article sur
SynchronizationContext
. La plupart du temps,SynchronizationContext.Current
:N'importe quel thread peut fixer ses propres
SynchronizationContext
, il y a donc des exceptions à la règle ci-dessus.Notez que par défaut,
Task
awaiter de planifier le reste de laasync
méthode sur l'actuelSynchronizationContext
si elle n'est pas null; sinon, il va sur le courantTaskScheduler
. Ce n'est pas si important aujourd'hui, mais dans un avenir proche il aura une distinction importante.J'ai écrit mes propres
async
/attendre
intro sur mon blog, et Stephen Toub a récemment publié un excellentasync
/attendre
FAQ.Sujet de la "simultanéité" vs "multithreading", voir ceci concerne, DONC, la question. Je dirais
async
permet la simultanéité, qui peut ou peut ne pas être multithread. Il est facile à utiliserawait Task.WhenAll
ouawait Task.WhenAny
de faire le traitement simultané, et, sauf si vous explicitement utiliser le pool de threads (par exemple,Task.Run
ouConfigureAwait(false)
), alors vous pouvez avoir simultanément plusieurs opérations en même temps (par exemple, e/S multiples ou d'autres types, comme laDelay
) - et il n'y a pas de fil nécessaire pour eux. J'utilise le terme "single-threaded simultanéité" pour ce genre de scénario, mais dans un ASP.NET l'hôte, vous pouvez effectivement se retrouver avec "zéro-thread simultanéité". Ce qui est assez doux.async /await fondamentalement simplifie la
ContinueWith
méthodes ( Suites en Continuation Passing Style )Elle n'introduit pas de simultanéité - vous avez encore à faire vous-même ( ou d'utiliser la version Asynchrone d'une méthode du cadre. )
Donc, le C# 5 version:
DoSomeWorkAsync()
retourne void ou quelque chose qui n'est pas awaitable. À partir de votre premier exemple, je suppose que c'est une méthode séquentielle que vous souhaitez exécuter sur un thread différent. Si vous avez changé le retour d'unTask
, sans introduire de la concurrence, alors oui, il pourrait bloquer. Dans le sens qu'elle ferait exécuter séquentiellement et d'être, tout comme normale code sur le thread d'INTERFACE utilisateur.await
seulement les rendements si la méthode retourne une awaitable qui n'a pas encore achevé.