web-dev-qa-db-fra.com

Devez-vous mettre Task.Run dans une méthode pour le rendre asynchrone?

J'essaie de comprendre l'attente asynchrone dans la forme la plus simple. Je veux créer une méthode très simple qui ajoute deux chiffres pour cet exemple, cela n’a aucun temps de traitement, c’est un problème, c’est juste une question de formulation.

Exemple 1

private async Task DoWork1Async()
{
    int result = 1 + 2;
}

Exemple 2

private async Task DoWork2Async()
{
    Task.Run( () =>
    {
        int result = 1 + 2;
    });
}

Si j'attends DoWork1Async() le code sera-t-il exécuté de manière synchrone ou asynchrone?

Dois-je envelopper le code de synchronisation avec Task.Run pour que la méthode soit attendue ET asynchrone afin de ne pas bloquer le thread d'interface utilisateur?

J'essaie de déterminer si ma méthode est une Task ou si je retourne Task<T> dois-je enrouler le code avec Task.Run pour le rendre asynchrone?.

Question stupide, j'en suis sûr, mais je vois des exemples sur le réseau où des personnes attendent un code qui n'a rien d'async et qui n'est pas encapsulé dans un Task.Run ou StartNew.

283
Neal

Commençons par clarifier la terminologie: "asynchrone" (async) signifie qu’elle peut céder le contrôle au thread appelant avant qu’il ne démarre. Dans une méthode async, ces points "de rendement" sont des expressions await.

Ce terme est très différent du terme "asynchrone", car utilisé dans la documentation MSDN pendant des années pour signifier "s'exécute sur un thread d'arrière-plan".

Pour confondre le problème, async est très différent de "waitable"; il existe certaines méthodes async dont les types de retour ne sont pas attendus, et de nombreuses méthodes renvoyant des types attendus qui ne sont pas async.

Assez parlé de ce qu'ils ne sont pas; voici ce qu'ils sont:

  • Le mot clé async autorise une méthode asynchrone (c'est-à-dire qu'il autorise les expressions await). Les méthodes async peuvent renvoyer Task, Task<T> ou (si vous devez) void.
  • Tout type qui suit un certain modèle peut être attendu. Les types les plus courants attendus sont Task et Task<T>.

Donc, si nous reformulons votre question en "comment puis-je exécuter une opération sur un thread d'arrière-plan d'une manière attendue", la réponse consiste à utiliser Task.Run:

private Task<int> DoWorkAsync() // No async because the method does not need await
{
  return Task.Run(() =>
  {
    return 1 + 2;
  });
}

(Mais ce modèle est une mauvaise approche; voir ci-dessous).

Mais si votre question est "comment créer une méthode async pouvant retourner à son appelant au lieu de bloquer", la réponse consiste à déclarer la méthode async et à utiliser await. "donner" des points:

private async Task<int> GetWebPageHtmlSizeAsync()
{
  var client = new HttpClient();
  var html = await client.GetAsync("http://www.example.com/");
  return html.Length;
}

Ainsi, le schéma de base des choses consiste à faire en sorte que le code async dépende de "l'attente" dans ses expressions await. Ces "attentes" peuvent être d’autres méthodes async ou juste des méthodes régulières renvoyant des attentes. Les méthodes classiques renvoyant Task/Task<T>can peuvent utiliser Task.Run pour exécuter le code sur un thread d'arrière-plan ou (plus généralement) elles peuvent utiliser TaskCompletionSource<T> ou un de ses raccourcis (TaskFactory.FromAsync, Task.FromResult, etc.). Je ne recommande pas d'emballer une méthode entière dans Task.Run; Les méthodes synchrones doivent avoir des signatures synchrones et il appartient au consommateur de décider si elles doivent être enveloppées dans un Task.Run:

private int DoWork()
{
  return 1 + 2;
}

private void MoreSynchronousProcessing()
{
  // Execute it directly (synchronously), since we are also a synchronous method.
  var result = DoWork();
  ...
}

private async Task DoVariousThingsFromTheUIThreadAsync()
{
  // I have a bunch of async work to do, and I am executed on the UI thread.
  var result = await Task.Run(() => DoWork());
  ...
}

J'ai un async/await intro sur mon blog; à la fin se trouvent de bonnes ressources de suivi. Les documents MSDN pour async sont exceptionnellement bons, aussi.

545
Stephen Cleary

Une des choses les plus importantes à retenir lors de la décoration d’une méthode avec async est qu’au moins il existe un attend opérateur à l'intérieur de la méthode. Dans votre exemple, je le traduirais comme indiqué ci-dessous en utilisant TaskCompletionSource .

private Task<int> DoWorkAsync()
{
    //create a task completion source
    //the type of the result value must be the same
    //as the type in the returning Task
    TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
    Task.Run(() =>
    {
        int result = 1 + 2;
        //set the result to TaskCompletionSource
        tcs.SetResult(result);
    });
    //return the Task
    return tcs.Task;
}

private async void DoWork()
{
    int result = await DoWorkAsync();
}
22
Ronald Ramos

Lorsque vous utilisez Task.Run pour exécuter une méthode, Task obtient un thread du pool de threads pour exécuter cette méthode. Donc, du point de vue du thread d'interface utilisateur, il est "asynchrone" car il ne bloque pas le thread d'interface utilisateur. Cela convient parfaitement aux applications de bureau car vous n'avez généralement pas besoin de beaucoup de threads pour prendre en charge les interactions utilisateur.

Toutefois, pour les applications Web, chaque requête est traitée par un thread de pool de threads. Par conséquent, le nombre de requêtes actives peut être augmenté en enregistrant ces threads. L'utilisation fréquente de threads de pool de threads pour simuler une opération asynchrone n'est pas évolutive pour les applications Web.

True Async n'implique pas nécessairement l'utilisation d'un thread pour les opérations d'E/S, telles que l'accès aux fichiers/bases de données, etc. Vous pouvez lire ceci pour comprendre pourquoi les opérations d'E/S n'ont pas besoin de threads. http://blog.stephencleary.com/2013/11/there-is-nnththead.html

Dans votre exemple simple, il s’agit d’un calcul purement lié au processeur. L’utilisation de Task.Run convient donc.

12
zheng yu