J'essaie de comprendre l'async attend sous la forme la plus simple. Je veux créer une méthode très simple qui ajoute deux nombres pour cet exemple, d'accord, ce n'est pas du tout un temps de traitement, c'est juste une question de formulation d'un exemple ici.
Exemple 1
private async Task DoWork1Async()
{
int result = 1 + 2;
}
Exemple 2
private async Task DoWork2Async()
{
Task.Run( () =>
{
int result = 1 + 2;
});
}
Si j'attends DoWork1Async()
, le code s'exécutera-t-il de manière synchrone ou asynchrone?
Dois-je envelopper le code de synchronisation avec Task.Run
pour rendre la méthode attendable ET asynchrone afin de ne pas bloquer le thread d'interface utilisateur?
J'essaie de savoir si ma méthode est un Task
ou retourne Task<T>
dois-je envelopper le code avec Task.Run
pour le rendre asynchrone.
Question stupide, je suis sûr, mais je vois des exemples sur le net où les gens attendent du code qui n'a rien d'asynchrone à l'intérieur et qui n'est pas enveloppé dans un Task.Run
ou StartNew
.
la source
Réponses:
Tout d'abord, clarifions un peu la terminologie: "asynchrone" (
async
) signifie qu'il peut redonner le contrôle au thread appelant avant qu'il ne démarre. Dans uneasync
méthode, ces points de "rendement" sont desawait
expressions.Ceci est très différent du terme "asynchrone", car (mis) utilisé par la documentation MSDN pendant des années pour signifier "s'exécute sur un thread d'arrière-plan".
Pour confondre encore la question,
async
est très différent de "attendable"; il existe desasync
méthodes dont les types de retour ne sont pas attendus, et de nombreuses méthodes qui renvoient des types attendus qui ne le sont pasasync
.Assez de ce qu'ils ne sont pas ; voici ce qu'ils sont :
async
mot-clé autorise une méthode asynchrone (c'est-à-dire qu'il autorise lesawait
expressions).async
méthodes peuvent revenirTask
,Task<T>
ou (si vous devez)void
.Task
etTask<T>
.Donc, si nous reformulons votre question en "comment puis-je exécuter une opération sur un thread d'arrière-plan d'une manière qui soit attendue", la réponse est d'utiliser
Task.Run
:(Mais ce modèle est une mauvaise approche; voir ci-dessous).
Mais si votre question est "comment puis-je créer une
async
méthode qui peut renvoyer à son appelant au lieu de bloquer", la réponse est de déclarer la méthodeasync
et de l'utiliserawait
pour ses points "donnant":Ainsi, le modèle de base des choses est de faire
async
dépendre le code des "attendables" dans sesawait
expressions. Ces "attendables" peuvent être d'autresasync
méthodes ou simplement des méthodes régulières renvoyant des attentes. Méthodes régulières de retourTask
/Task<T>
peuvent utiliserTask.Run
pour exécuter du code sur un thread d'arrière - plan, ou (plus souvent) , ils peuvent utiliserTaskCompletionSource<T>
ou l' un de ses raccourcis (TaskFactory.FromAsync
,Task.FromResult
, etc.). Je ne recommande pas d' envelopper une méthode entière dansTask.Run
; les méthodes synchrones doivent avoir des signatures synchrones, et il convient de laisser au consommateur le soin de les inclure dans unTask.Run
:J'ai une
async
/await
intro sur mon blog; à la fin, il y a de bonnes ressources de suivi. Les documents MSDN pourasync
sont également exceptionnellement bons.la source
async
les méthodes doivent revenirTask
,Task<T>
ouvoid
.Task
etTask<T>
sont attendus;void
n'est pas.async void
signature de méthode sera compilée, c'est juste une idée assez terrible car vous perdez votre pointeur vers votre tâche asynchronevoid
n'est pas attendu.Task.Run
(commeDoWorkAsync
dans cette réponse). UtiliserTask.Run
pour appeler une méthode à partir d'un contexte d'interface utilisateur est approprié (commeDoVariousThingsFromTheUIThreadAsync
).Task.Run
pour invoquer une méthode, mais s'il y aTask.Run
tout ou presque tout le code de la méthode, alors c'est un anti-modèle - il suffit de garder cette méthode synchrone et de la déplacerTask.Run
vers le haut.L'une des choses les plus importantes à retenir lors de la décoration d'une méthode avec async est qu'au moins il y a un opérateur d' attente à l'intérieur de la méthode. Dans votre exemple, je le traduirais comme indiqué ci-dessous en utilisant TaskCompletionSource .
la source
Lorsque vous utilisez Task.Run pour exécuter une méthode, Task obtient un thread de threadpool pour exécuter cette méthode. Du point de vue du thread d'interface utilisateur, il est donc "asynchrone" car il ne bloque pas le thread d'interface utilisateur. C'est très bien pour une application de bureau car vous n'avez généralement pas besoin de nombreux threads pour gérer les interactions des utilisateurs.
Cependant, pour une application Web, chaque demande est traitée par un thread de pool de threads et le nombre de requêtes actives peut donc être augmenté en enregistrant ces threads. L'utilisation fréquente de threads de pool de threads pour simuler une opération asynchrone n'est pas évolutive pour les applications Web.
True Async n'implique pas nécessairement l'utilisation d'un thread pour les opérations d'E / S, telles que l'accès aux fichiers / bases de données, etc. Vous pouvez lire ceci pour comprendre pourquoi l'opération d'E / S n'a pas besoin de threads. http://blog.stephencleary.com/2013/11/there-is-no-thread.html
Dans votre exemple simple, il s'agit d'un calcul pur lié au processeur, donc l'utilisation de Task.Run est très bien.
la source
I should NOT wrap the synchronous call in Task.Run()
c'est correct. Si vous le faites, vous changerez simplement de thread. c'est-à-dire que vous débloquez le thread de requête initial mais que vous prenez un autre thread du pool de threads qui aurait pu être utilisé pour traiter une autre requête. Le seul résultat est une surcharge de changement de contexte lorsque l'appel est terminé pour un gain absolument nul