Je viens de recevoir VS2012 et j'essaie de comprendre async
.
Disons que j'ai une méthode qui récupère une valeur à partir d'une source de blocage. Je ne veux pas que l'appelant de la méthode bloque. Je pourrais écrire la méthode pour prendre un rappel qui est appelé lorsque la valeur arrive, mais comme j'utilise C # 5, je décide de rendre la méthode asynchrone afin que les appelants n'aient pas à gérer les rappels:
// contrived example (edited in response to Servy's comment)
public static Task<string> PromptForStringAsync(string prompt)
{
return Task.Factory.StartNew(() => {
Console.Write(prompt);
return Console.ReadLine();
});
}
Voici un exemple de méthode qui l'appelle. Si ce PromptForStringAsync
n'était pas asynchrone, cette méthode nécessiterait l'imbrication d'un rappel dans un rappel. Avec async, j'écris ma méthode de cette manière très naturelle:
public static async Task GetNameAsync()
{
string firstname = await PromptForStringAsync("Enter your first name: ");
Console.WriteLine("Welcome {0}.", firstname);
string lastname = await PromptForStringAsync("Enter your last name: ");
Console.WriteLine("Name saved as '{0} {1}'.", firstname, lastname);
}
Jusqu'ici tout va bien. Le problème est lorsque j'appelle GetNameAsync:
public static void DoStuff()
{
GetNameAsync();
MainWorkOfApplicationIDontWantBlocked();
}
Tout l'intérêt de GetNameAsync
est qu'il est asynchrone. Je ne veux pas qu'il se bloque, car je veux revenir à MainWorkOfApplicationIDontWantBlocked dès que possible et laisser GetNameAsync faire son travail en arrière-plan. Cependant, l'appeler de cette façon me donne un avertissement du compilateur sur la GetNameAsync
ligne:
Warning 1 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
Je suis parfaitement conscient que "l'exécution de la méthode actuelle se poursuit avant que l'appel ne soit terminé". C'est le but du code asynchrone, non?
Je préfère que mon code compile sans avertissements, mais il n'y a rien à "corriger" ici car le code fait exactement ce que je compte faire. Je peux me débarrasser de l'avertissement en stockant la valeur de retour de GetNameAsync
:
public static void DoStuff()
{
var result = GetNameAsync(); // supress warning
MainWorkOfApplicationIDontWantBlocked();
}
Mais maintenant, j'ai du code superflu. Visual Studio semble comprendre que j'ai été obligé d'écrire ce code inutile, car il supprime l'avertissement normal «valeur jamais utilisée».
Je peux également me débarrasser de l'avertissement en enveloppant GetNameAsync dans une méthode qui n'est pas asynchrone:
public static Task GetNameWrapper()
{
return GetNameAsync();
}
Mais c'est du code encore plus superflu. Je dois donc écrire du code dont je n'ai pas besoin ou que je tolère un avertissement inutile.
Y a-t-il quelque chose dans mon utilisation de l'async qui ne va pas ici?
la source
PromptForStringAsync
vous faites plus de travail que nécessaire; renvoyez simplement le résultat deTask.Factory.StartNew
. C'est déjà une tâche dont la valeur est la chaîne saisie dans la console. Il n'est pas nécessaire de l'attendre pour renvoyer le résultat; cela n'ajoute aucune valeur nouvelle.GetNameAsync
fournir le nom complet qui a été fourni par l'utilisateur ( par exempleTask<Name>
, plutôt que de retourner unTask
?DoStuff
Pourrait alors stocker cette tâche, et soitawait
il après l'autre méthode, ou même passer la tâche à l'autre méthode de sorte qu'il pourraitawait
ouWait
quelque part à l' intérieur de la mise en œuvre de ce.async
mot - clé.Réponses:
Si vous n'avez vraiment pas besoin du résultat, vous pouvez simplement changer la
GetNameAsync
signature de pour retournervoid
:Pensez à voir la réponse à une question connexe: Quelle est la différence entre renvoyer une annulation et renvoyer une tâche?
Mettre à jour
Si vous avez besoin du résultat, vous pouvez modifier le
GetNameAsync
pour renvoyer, par exempleTask<string>
:Et utilisez-le comme suit:
la source
GetNameAsync
ne retourne aucune valeur (sauf le résultat lui-même, bien sûr).void
, il n'a aucun moyen de savoir quand c'est fait. C'est ce que je voulais dire quand j'ai dit «le résultat» dans mon commentaire précédent.async void
méthode à l'exception des gestionnaires d'événements.async void
une exception que vous n'attrapez pas, cela plantera votre processus, mais dans .net 4.5, il continuera à fonctionner.Je suis assez en retard dans cette discussion, mais il y a aussi la possibilité d'utiliser la
#pragma
directive pré-processeur. J'ai du code asynchrone ici et là que je ne veux explicitement pas attendre dans certaines conditions, et je n'aime pas les avertissements et les variables inutilisées, tout comme vous:Le
"4014"
provient de cette page MSDN: Avertissement du compilateur (niveau 1) CS4014 .Voir également l'avertissement / réponse de @ ryan-horath ici https://stackoverflow.com/a/12145047/928483 .
Mise à jour pour C # 7.0
C # 7.0 ajoute une nouvelle fonctionnalité, supprimer les variables: Discards - Guide C # , qui peut également aider à cet égard.
la source
var
, il suffit d'écrire_ = SomeMethodAsync();
Je n'aime pas particulièrement les solutions qui affectent la tâche à une variable inutilisée ou modifient la signature de la méthode pour renvoyer void. Le premier crée un code superflu et non intuitif, tandis que le second peut ne pas être possible si vous implémentez une interface ou si vous avez une autre utilisation de la fonction où vous souhaitez utiliser la tâche retournée.
Ma solution est de créer une méthode d'extension de Task, appelée DoNotAwait () qui ne fait rien. Cela supprimera non seulement tous les avertissements, ReSharper ou autre, mais rendra le code plus compréhensible et indiquera aux futurs responsables de votre code que vous aviez vraiment l'intention que l'appel ne soit pas attendu.
Méthode d'extension:
Usage:
Modifié pour ajouter: c'est similaire à la solution de Jonathan Allen où la méthode d'extension commencerait la tâche si elle n'était pas déjà commencée, mais je préfère avoir des fonctions à usage unique afin que l'intention de l'appelant soit complètement claire.
la source
async void
EST MAUVAIS!Ce que je suggère, c'est que vous exécutiez explicitement le
Task
via une méthode anonyme ...par exemple
Ou si vous vouliez le bloquer, vous pouvez attendre sur la méthode anonyme
Cependant, si votre
GetNameAsync
méthode doit interagir avec l'interface utilisateur ou même quoi que ce soit lié à l'interface utilisateur, (WINRT / MVVM, je vous regarde), alors cela devient un peu plus funk =)Vous devrez transmettre la référence au répartiteur de l'interface utilisateur comme ceci ...
Et puis, dans votre méthode asynchrone, vous devrez interagir avec votre interface utilisateur ou les éléments liés à l'interface utilisateur pensant que le répartiteur ...
la source
This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
cela entraîne également la création d'un nouveau thread, alors qu'un nouveau thread ne sera pas nécessairement créé avec async / await seul.C'est ce que je fais actuellement:
Où
RunConcurrently
est défini comme ...https://github.com/docevaad/Anchor/blob/master/Tortuga.Anchor/Tortuga.Anchor.source/shared/TaskUtilities.cs
https://www.nuget.org/packages/Tortuga.Anchor/
la source
public static void Forget(this Task task) { }
async Task
. Certaines tâches doivent être démarrées manuellement.Selon l'article de Microsoft sur cet avertissement, vous pouvez le résoudre en affectant simplement la tâche renvoyée à une variable. Vous trouverez ci-dessous une traduction du code fourni dans l'exemple Microsoft:
Notez que cela entraînera le message «La variable locale n'est jamais utilisée» dans ReSharper.
la source
Task
-les fonctions de retour devraient êtreawait
-ed à moins que vous n'ayez une très bonne raison de ne pas le faire. Il n'y a aucune raison ici pour que le rejet de la tâche soit mieux que la réponse déjà acceptée d'utiliser uneasync void
méthode.async void
introduit de graves problèmes autour de la gestion des erreurs et entraîne un code non testable (voir mon article MSDN ). Il serait de loin préférable d'utiliser la variable - si vous êtes absolument sûr de vouloir que les exceptions soient avalées en silence. Plus vraisemblablement, l’opérateur voudrait démarrer deuxTask
s, puis faire unawait Task.WhenAll
.async void DoNotWait(Task t) { await t; }
méthode d'assistance simple peut être utilisée pour éviter les inconvénients desasync void
méthodes que vous décrivez. (Et je ne pense pas queTask.WhenAll
ce soit ce que veut l'OP, mais cela pourrait très bien l'être.)Ici, une solution simple.
Cordialement
la source
C'est votre exemple simplifié qui provoque le code superfleux. Normalement, vous voudriez utiliser les données qui ont été extraites de la source de blocage à un moment donné dans le programme, donc vous voudriez que le résultat soit renvoyé afin qu'il soit possible d'accéder aux données.
Si vous avez vraiment quelque chose qui se passe totalement isolé du reste du programme, async ne serait pas la bonne approche. Démarrez simplement un nouveau fil pour cette tâche.
la source
async
été conçu pour nettoyer ( par exemple )MethodWithCallback((result1) => { Use(result1); MethodWithCallback((result2) => { Use(result1,result2); })
même dans cet exemple trivial, c'est une salope à analyser. Avec async, un code équivalent est généré pour moi lorsque j'écris,result1 = await AsyncMethod(); Use(result1); result2 = await AsyncMethod(); Use(result1,result2);
ce qui est beaucoup plus facile à lire (bien qu'aucun des deux ne soit très lisible brisé ensemble dans ce commentaire!)Use
.Voulez-vous vraiment ignorer le résultat? comme en ignorant les exceptions inattendues?
Sinon, vous voudrez peut-être jeter un coup d'œil à cette question: approche Fire and Forget ,
la source
Si vous ne souhaitez pas modifier la signature de la méthode à renvoyer
void
(car le retourvoid
doit toujours être un void ed), vous pouvez utiliser la fonctionnalité C # 7.0+ Discard comme celle-ci, qui est légèrement meilleure que l'assignation à une variable (et devrait supprimer la plupart des autres avertissements des outils de validation source):la source