Dans cet article MSDN , l'exemple de code suivant est fourni (légèrement modifié par souci de concision):
public async Task<ActionResult> Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Department department = await db.Departments.FindAsync(id);
if (department == null)
{
return HttpNotFound();
}
return View(department);
}
La FindAsync
méthode récupère un Department
objet par son ID et renvoie a Task<Department>
. Ensuite, le département est immédiatement vérifié pour voir s'il est nul. Si je comprends bien, demander la valeur de la tâche de cette manière bloquera l'exécution du code jusqu'à ce que la valeur de la méthode attendue soit renvoyée, ce qui en fait un appel synchrone.
Pourquoi voudriez-vous faire ça? Ne serait-il pas plus simple d'appeler simplement la méthode synchrone Find(id)
, si vous voulez bloquer immédiatement de toute façon?
c#
.net
asp.net-mvc
async
Robert Harvey
la source
la source
... else return null;
Ensuite, vous devez vérifier que la méthode a effectivement trouvé le service que vous avez demandé.Réponses:
Pas assez.
Lorsque vous appelez,
await db.Departments.FindAsync(id)
la tâche est envoyée et le thread actuel est renvoyé au pool pour être utilisé par d'autres opérations. Le flux d'exécution est bloqué (comme il le serait indépendamment de l'utilisationdepartment
juste après, si je comprends bien), mais le thread lui-même est libre d'être utilisé par d'autres choses pendant que vous attendez que l'opération soit terminée hors machine (et signalée par un événement ou un port d'achèvement).Si vous avez appelé,
d.Departments.Find(id)
le thread reste assis et attend la réponse, même si la plupart du traitement est effectué sur la base de données.Vous libérez efficacement les ressources du processeur lorsque le disque est lié.
la source
await
qui était fait était de signer sur le reste de la méthode en tant que continuation sur le même thread (il y a des exceptions; certaines méthodes asynchrones font tourner leur propre thread), ou de signer laasync
méthode en tant que continuation sur le même thread et autoriser la code restant à exécuter (comme vous pouvez le voir, je ne suis pas clair sur leasync
fonctionnement). Ce que vous décrivez ressemble plus à une forme sophistiquée deThread.Sleep(untilReturnValueAvailable)
ConfigureAwait
iirc).await
son appel àpublic async Task<ActionResult> Details(int? id)
. Sinon, l'appel d'origine sera simplement bloqué, en attentedepartment == null
de résolution.await ...
"retour", l'FindAsync
appel est terminé. C'est ce qui vous attend. Cela s'appelle attendre car cela fait attendre votre code. (Mais notez que ce n'est pas la même chose que de faire attendre le thread actuel)Je déteste vraiment qu'aucun des exemples ne montre comment il est possible d'attendre quelques lignes avant d'attendre la tâche. Considère ceci.
C'est le genre de code que les exemples encouragent, et vous avez raison. Cela a peu de sens. Cela libère le thread principal pour faire d'autres choses, comme répondre à l'entrée de l'interface utilisateur, mais la vraie puissance de l'async / wait est que je peux facilement continuer à faire d'autres choses pendant que j'attends qu'une tâche potentiellement longue soit terminée. Le code ci-dessus "bloquera" et attendra pour exécuter la ligne d'impression jusqu'à ce que nous ayons obtenu Foo & Bar. Il n'est pas nécessaire d'attendre cependant. Nous pouvons traiter cela en attendant.
Maintenant, avec le code réécrit, nous ne nous arrêtons pas et n'attendons pas nos valeurs jusqu'à ce que nous le devions. Je suis toujours à la recherche de ce genre d'opportunités. Être intelligent quand nous attendons peut entraîner des améliorations significatives des performances. Nous avons plusieurs cœurs de nos jours, autant les utiliser.
la source
await
laisser le fil faire des choses complètement différentes à la place.Il y a donc plus de choses qui se passent en coulisses ici. Async / Await est du sucre syntaxique. Regardez d'abord la signature de la fonction FindAsync. Il renvoie une tâche. Vous voyez déjà la magie du mot-clé, il déballe cette tâche dans un département.
La fonction appelante ne bloque pas. Ce qui se passe, c'est que l'affectation au département et tout ce qui suit le mot-clé wait est encadrée dans une fermeture et à toutes fins utiles passée à la méthode Task.ContinueWith (la fonction FindAsync est automatiquement exécutée sur un autre thread).
Bien sûr, il se passe plus de choses en arrière-plan, car l'opération est redirigée vers le thread d'origine (vous n'avez donc plus à vous soucier de la synchronisation avec l'interface utilisateur lors de l'exécution d'une opération en arrière-plan) et dans le cas où la fonction appelante est Async ( et étant appelé de manière asynchrone), la même chose se produit dans la pile.
Donc, ce qui se passe, c'est que vous obtenez la magie des opérations Async, sans les pièges.
la source
Non, ça ne revient pas immédiatement. L'attente rend l'appel de méthode asynchrone. Lorsque FindAsync est appelé, la méthode Details retourne avec une tâche qui n'est pas terminée. Une fois FindAsync terminé, il renvoie son résultat dans la variable department et reprend le reste de la méthode Details.
la source
async
await
ne crée généralement pas de nouveaux threads, et même si c'est le cas, vous devez toujours attendre la valeur de ce département pour savoir si elle est nulle.public async Task<ActionResult>
doit également êtreawait
await
ne doit pas être mélangé avec.Wait()
ou.Result
, car cela peut provoquer des blocages. La chaîne asynchrone / attente se termine généralement par une fonction avec uneasync void
signature, qui est principalement utilisée pour les gestionnaires d'événements ou pour les fonctions appelées directement par les éléments d'interface utilisateur.J'aime à penser à "async" comme un contrat, un contrat qui dit "je peux l'exécuter de manière asynchrone si vous en avez besoin, mais vous pouvez aussi m'appeler comme n'importe quelle autre fonction synchrone".
Autrement dit, un développeur faisait des fonctions et certaines décisions de conception les ont amenés à créer / marquer un tas de fonctions comme "asynchrones". L'appelant / consommateur des fonctions est libre de les utiliser à sa guise. Comme vous le dites, vous pouvez soit appeler wait juste avant l'appel de fonction et l'attendre, de cette façon vous l'avez traité comme une fonction synchrone, mais si vous le souhaitez, vous pouvez l'appeler sans attendre car
et après, disons, 10 lignes vers le bas de la fonction que vous appelez
le traitant donc comme une fonction asynchrone.
C'est à vous.
la source
"si vous voulez bloquer immédiatement", la réponse est "Oui". Uniquement lorsque vous avez besoin d'une réponse rapide, attendre / async est logique. Par exemple, le thread d'interface utilisateur arrive à une méthode vraiment asynchrone, le thread d'interface utilisateur retournera et continuera à écouter le clic du bouton, tandis que le code ci-dessous "attendre" sera excuté par un autre thread et obtiendra finalement le résultat.
la source