Je suis tombé sur quelques bonnes pratiques pour la programmation asynchrone en utilisant les mots async
- await
clés / c # (je suis nouveau sur c # 5.0).
L'un des conseils donnés était le suivant:
Stabilité: connaissez vos contextes de synchronisation
... Certains contextes de synchronisation sont non réentrants et monothread. Cela signifie qu'une seule unité de travail peut être exécutée dans le contexte à un moment donné. Le thread d'interface utilisateur Windows ou le contexte de demande ASP.NET en est un exemple. Dans ces contextes de synchronisation à un seul thread, il est facile de se bloquer soi-même. Si vous créez une tâche à partir d'un contexte à thread unique, puis attendez cette tâche dans le contexte, votre code d'attente peut bloquer la tâche en arrière-plan.
public ActionResult ActionAsync()
{
// DEADLOCK: this blocks on the async task
var data = GetDataAsync().Result;
return View(data);
}
private async Task<string> GetDataAsync()
{
// a very simple async method
var result = await MyWebService.GetDataAsync();
return result.ToString();
}
Si j'essaie de le disséquer moi-même, le thread principal apparaît dans un nouveau MyWebService.GetDataAsync();
, mais comme le thread principal y attend, il attend le résultat GetDataAsync().Result
. En attendant, disons que les données sont prêtes. Pourquoi le thread principal ne poursuit-il pas sa logique de continuation et renvoie-t-il un résultat de chaîne GetDataAsync()
?
Quelqu'un peut-il m'expliquer pourquoi il y a une impasse dans l'exemple ci-dessus? Je ne sais absolument pas quel est le problème ...
la source
var data = GetDataAsync().Result;
est une ligne de code qui ne devrait jamais être faite dans un contexte que vous ne devriez pas bloquer (requête UI ou ASP.NET). Même s'il ne se bloque pas, il bloque le thread pour une durée indéterminée. Donc, fondamentalement, c'est un exemple terrible. [Vous devez quitter le thread de l'interface utilisateur avant d'exécuter un code comme celui-ci, ou l'utiliserawait
aussi, comme le suggère Toni.]Réponses:
Jetez un œil à cet exemple , Stephen a une réponse claire pour vous:
Un autre lien que vous devriez lire: Attendez, et l'interface utilisateur et les blocages! Oh mon!
la source
GetDataAsync().Result;
s'exécutera lorsque la tâche retournée par seraGetDataAsync()
terminée, en attendant, il bloque le thread de l'interface utilisateurreturn result.ToString()
) est mise en file d'attente dans le thread de l'interface utilisateur pour exécutionGetDataAsync()
se terminera lorsque sa continuation en file d'attente sera exécutéeImpasse!
Le blocage peut être brisé par des alternatives fournies pour éviter le fait 1 ou le fait 2.
var data = await GetDataAsync()
, ce qui permet au thread d'interface utilisateur de continuer à s'exécutervar data = Task.Run(GetDataAsync).Result
, qui affichera la suite dans le contexte de synchronisation d'un thread de threadpool. Cela permet à la tâche renvoyée parGetDataAsync()
de se terminer.Ceci est très bien expliqué dans un article de Stephen Toub , à peu près à mi-chemin où il utilise l'exemple de
DelayAsync()
.la source
var data = Task.Run(GetDataAsync).Result
c'est nouveau pour moi. J'ai toujours pensé que l'extérieur.Result
serait facilement disponible dès que la première attente deGetDataAsync
est atteinte, il ledata
sera toujoursdefault
. Intéressant.J'étais juste en train de jouer avec ce problème à nouveau dans un projet ASP.NET MVC. Lorsque vous souhaitez appeler des
async
méthodes à partir de aPartialView
, vous n'êtes pas autorisé à créer le fichierPartialView
async
. Vous obtiendrez une exception si vous le faites.Vous pouvez utiliser la solution de contournement simple suivante dans le scénario où vous souhaitez appeler une
async
méthode à partir d' une méthode de synchronisation:SynchronizationContext
SynchronizationContext
Exemple:
la source
Un autre point principal est que vous ne devez pas bloquer sur les tâches, et utiliser async complètement pour éviter les blocages. Ensuite, ce sera tout blocage asynchrone et non synchrone.
la source
Une solution à laquelle je suis arrivé est d'utiliser une
Join
méthode d'extension sur la tâche avant de demander le résultat.Le code ressemble à ceci:
Où est la méthode de jointure:
Je ne suis pas assez dans le domaine pour voir les inconvénients de cette solution (le cas échéant)
la source