485 votes

Comment à en toute sécurité appellent une méthode asynchrone en c# sans attendre

J'ai un async méthode qui ne retourne pas de données:

public async Task MyAsyncMethod()
{
    // do some stuff async, don't return any data
}

J'appelle cela de l'autre méthode qui retourne des données:

public string GetStringData()
{
    MyAsyncMethod(); // this generates a warning and swallows exceptions
    return "hello world";
}

Appelant MyAsyncMethod() sans attendre, il provoque un "Parce que cet appel n'est pas attendue, la méthode actuelle continue de s'exécuter avant l'appel est terminé" avertissement dans visual studio. Sur la page de cet avertissement, il est indiqué:

Vous devriez envisager la suppression de l'avertissement que si vous êtes sûr que vous ne voulez pas attendre pour l'appel asynchrone terminer et que la méthode appelée de ne pas soulever toutes les exceptions.

Je suis sûr que je ne veux pas attendre que l'appel à la fin; je n'ai pas besoin de ou le temps de. Mais l'appel peut lever des exceptions.

J'ai rencontré ce problème plusieurs fois et je suis sûr que c'est un problème commun qui doit avoir une solution commune.

Comment puis-je en toute sécurité de l'appel d'une méthode asynchrone sans attendre le résultat?

Mise à jour:

Pour les personnes de suggérer que j'ai juste à attendre le résultat, c'est le code qui est de répondre à une requête web sur notre service web, ASP.NET l'API Web). En attendant, dans un contexte de l'INTERFACE utilisateur conserve le thread d'INTERFACE utilisateur gratuit, mais en attente d'une requête web appel d'attendre la fin de la Tâche avant de répondre à la demande, augmentant ainsi les temps de réponse avec pas de raison.

287voto

Peter Ritchie Points 18352

Si vous souhaitez obtenir de l'exception "asynchrone", que vous pouvez faire:

  MyAsyncMethod().
    ContinueWith(t => Console.WriteLine(t.Exception),
        TaskContinuationOptions.OnlyOnFaulted);

Cela vous permettra de traiter avec une exception sur un thread autre que le "principal" thread. Cela signifie que vous n'avez pas à "attendre" pour l'appel à laMyAsyncMethod() de la thread qui appelle MyAsyncMethod; mais vous permet toujours de faire quelque chose avec une exception, mais seulement si une exception se produit.

Mise à jour:

techniquement, vous pourriez faire quelque chose de similaire avec await:

try
{
    await MyAsyncMethod().ConfigureAwait(false);
}
catch (Exception ex)
{
    Trace.WriteLine(ex);
}

...ce qui serait utile si vous avez besoin d'utiliser spécifiquement try/catch (ou using) mais je trouve l' ContinueWith être un peu plus explicite, car vous devez savoir ce qu' ConfigureAwait(false) moyens.

102voto

Stephen Cleary Points 91731

D'abord, vous devriez envisager de faire de la GetStringData un async méthode et l'ont await la tâche retourné à partir de MyAsyncMethod.

Si vous êtes absolument sûr que vous n'avez pas besoin de gérer les exceptions de MyAsyncMethod ou de savoir quand elle est terminée, alors vous pouvez faire ceci:

public string GetStringData()
{
  var _ = MyAsyncMethod();
  return "hello world";
}

BTW, ce n'est pas un "problème commun". Il est très rare de vouloir exécuter un code et pas de soins si elle complète et de ne pas se préoccuper de savoir si elle se termine avec succès.

Mise à jour:

Puisque vous êtes sur le ASP.NET et vouloir rentrer tôt, vous pouvez trouver mon billet de blog sur le sujet utile. Cependant, ASP.NET n'a pas été conçu pour cela, et il n'y a aucune garantie que votre code sera exécuté après que la réponse est renvoyée. ASP.NET fera de son mieux pour le laisser courir, mais il ne peut pas le garantir.

Donc, c'est une bonne solution pour quelque chose de simple comme jeter un événement dans un journal où il n'a pas vraiment d'importance si vous perdez un peu ici et là. Ce n'est pas une bonne solution pour tout type d'entreprise opérations critiques. Dans ces situations, vous devez adopter une architecture plus complexe, avec une persistante moyen d'économiser de l'exploitation (par exemple, les Files d'attente Azure, MSMQ) et un autre processus en arrière-plan (par exemple, Azure Rôle de Travailleur, Service Win32) pour les traiter.

63voto

George Powell Points 688

La réponse de Peter Ritchie était ce que je voulais, et Stephen Cleary l'article sur le retour au début ASP.NET a été très utile.

Comme un problème plus général (pas spécifique à une ASP.NET contexte) l'application de Console suivante illustre l'utilisation et le comportement de la réponse de Pierre à l'aide de Task.ContinueWith(...)

static void Main(string[] args)
{
  try
  {
    // output "hello world" as method returns early
    Console.WriteLine(GetStringData());
  }
  catch
  {
    // Exception is NOT caught here
  }
  Console.ReadLine();
}

public static string GetStringData()
{
  MyAsyncMethod().ContinueWith(OnMyAsyncMethodFailed, TaskContinuationOptions.OnlyOnFaulted);
  return "hello world";
}

public static async Task MyAsyncMethod()
{
  await Task.Run(() => { throw new Exception("thrown on background thread"); });
}

public static void OnMyAsyncMethodFailed(Task task)
{
  Exception ex = task.Exception;
  // Deal with exceptions here however you want
}

GetStringData() retourne tôt sans attendre d' MyAsyncMethod() et les exceptions levées en MyAsyncMethod() sont traités en OnMyAsyncMethodFailed(Task task) et pas dans l' try/catch autour GetStringData()

1voto

rhughes Points 3325

Je suppose que la question se pose, pourquoi auriez-vous besoin de faire cela? La raison de async dans C # 5.0 est que vous pouvez attendre un résultat. Cette méthode n'est pas réellement asynchrone, mais simplement appelée à la fois pour ne pas trop interférer avec le thread en cours.

Peut-être vaudra-t-il mieux commencer un fil et le laisser finir seul.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X