Ce n'est pas un doublon de "Comment appeler une méthode asynchrone en C # sans attendre" .
Comment puis-je supprimer correctement l'avertissement suivant?
avertissement CS4014: cet appel n'étant pas attendu, l'exécution de la méthode en cours se poursuit avant la fin de l'appel. Pensez à appliquer l'opérateur 'wait' au résultat de l'appel.
Un exemple simple:
static async Task WorkAsync()
{
await Task.Delay(1000);
Console.WriteLine("Done!");
}
static async Task StartWorkAsync()
{
WorkAsync(); // I want fire-and-forget
// more unrelated async/await stuff here, e.g.:
// ...
await Task.Delay(2000);
}
Ce que j'ai essayé et que je n'ai pas aimé:
static async Task StartWorkAsync()
{
#pragma warning disable 4014
WorkAsync(); // I want fire-and-forget here
#pragma warning restore 4014
// ...
}
static async Task StartWorkAsync()
{
var ignoreMe = WorkAsync(); // I want fire-and-forget here
// ...
}
Mis à jour , puisque la réponse acceptée d'origine a été modifiée, j'ai modifié la réponse acceptée en celle en utilisant C # 7.0) , car je ne pense pas que ContinueWith
soit approprié ici. Chaque fois que je dois enregistrer des exceptions pour des opérations feu et oublie, j'utilise une approche plus élaborée proposé par Stephen Cleary ici .
Avec C # 7, vous pouvez maintenant utiliser Rejeté :
_ = WorkAsync();
Vous pouvez créer une méthode d'extension empêchant l'avertissement. La méthode d'extension peut être vide ou vous pouvez ajouter le traitement des exceptions avec .ContinueWith()
.
static class TaskExtensions
{
public static void Forget(this Task task)
{
task.ContinueWith(
t => { WriteLog(t.Exception); },
TaskContinuationOptions.OnlyOnFaulted);
}
}
public async Task StartWorkAsync()
{
this.WorkAsync().Forget();
}
Cependant ASP.NET compte le nombre de tâches en cours d'exécution. Il ne fonctionnera donc pas avec la simple extension Forget()
indiquée ci-dessus et à la place. peut échouer avec l'exception:
Un module ou un gestionnaire asynchrone s'est terminé alors qu'une opération asynchrone était toujours en attente.
Avec .NET 4.5.2, il peut être résolu en utilisant HostingEnvironment.QueueBackgroundWorkItem
:
public static Task HandleFault(this Task task, CancellationToken cancelToken)
{
return task.ContinueWith(
t => { WriteLog(t.Exception); },
cancelToken,
TaskContinuationOptions.OnlyOnFaulted,
TaskScheduler.Default);
}
public async Task StartWorkAsync()
{
System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(
cancelToken => this.WorkAsync().HandleFault(cancelToken));
}
Vous pouvez décorer la méthode avec l'attribut suivant:
[System.Diagnostics.CodeAnalysis.SuppressMessage("Await.Warning", "CS4014:Await.Warning")]
static async Task StartWorkAsync()
{
WorkAsync();
// ...
}
En gros, vous dites au compilateur que vous savez ce que vous faites et qu'il n'a pas besoin de vous inquiéter d'une éventuelle erreur.
La partie importante de ce code est le deuxième paramètre. La partie "CS4014:" est ce qui supprime l'avertissement. Vous pouvez écrire tout ce que vous voulez sur le reste.
Mes deux façons de traiter cela.
Enregistrez-le dans une variable de rejet (C # 7)
Exemple
_ = Task.Run(() => DoMyStuff()).ConfigureAwait(false);
Depuis l'introduction des rejets en C # 7, je considère maintenant que c'est mieux que de supprimer l'avertissement. Parce que cela supprime non seulement l'avertissement, mais clarifie également l'intention de feu et d'oubli.
De plus, le compilateur pourra l'optimiser en mode release.
Il suffit de le supprimer
#pragma warning disable 4014
est une solution suffisamment agréable pour "tirer et oublier".
Cet avertissement existe parce que, dans de nombreux cas, votre intention n’est pas d’utiliser une méthode qui renvoie une tâche sans l’attendre. Supprimer l'avertissement lorsque vous avez l'intention de tirer et d'oublier est logique.
Si vous avez du mal à vous rappeler comment épeler #pragma warning disable 4014
, laissez simplement Visual Studio l’ajouter pour vous. Appuyez sur Ctrl +. ouvrir "Actions rapides" puis "Supprimer CS2014"
dans l'ensemble
C'est stupide de créer une méthode qui nécessite quelques ticks de plus, juste pour supprimer un avertissement.
Un moyen facile d’arrêter l’avertissement consiste simplement à affecter la tâche lorsqu’elle l’appelle:
Task fireAndForget = WorkAsync(); // No warning now
Et dans votre message original, vous feriez:
static async Task StartWorkAsync()
{
// Fire and forget
var fireAndForget = WorkAsync(); // Tell the compiler you know it's a task that's being returned
// more unrelated async/await stuff here, e.g.:
// ...
await Task.Delay(2000);
}
La raison de l'avertissement est que WorkAsync renvoie un Task
qui n'est jamais lu ni attendu. Vous pouvez définir le type de retour de WorkAsync sur void
et l'avertissement disparaîtra.
Généralement, une méthode retourne un Task
lorsque l'appelant a besoin de connaître le statut du travailleur. Dans le cas d'un incendie et oublie, vide devrait être retourné à ressembler à ce que l'appelant est indépendant de la méthode appelée.
static async void WorkAsync()
{
await Task.Delay(1000);
Console.WriteLine("Done!");
}
static async Task StartWorkAsync()
{
WorkAsync(); // no warning since return type is void
// more unrelated async/await stuff here, e.g.:
// ...
await Task.Delay(2000);
}
Pourquoi ne pas l'envelopper dans une méthode asynchrone qui renvoie void? Un peu long mais toutes les variables sont utilisées.
static async Task StartWorkAsync()
{
async void WorkAndForgetAsync() => await WorkAsync();
WorkAndForgetAsync(); // no warning
}
J'ai trouvé cette approche par accident aujourd'hui. Vous pouvez définir un délégué et affecter d'abord la méthode async au délégué.
delegate Task IntermediateHandler();
static async Task AsyncOperation()
{
await Task.Yield();
}
et appelez comme si
(new IntermediateHandler(AsyncOperation))();
...
J'ai trouvé intéressant de noter que le compilateur ne donnerait pas exactement le même avertissement lors de l'utilisation du délégué.