221 votes

Quelle est la différence entre Task.Start/Wait et Async/Await ?

J'ai peut-être raté quelque chose mais quelle est la différence entre faire :

public void MyMethod()
{
  Task t = Task.Factory.StartNew(DoSomethingThatTakesTime);
  t.Wait();
  UpdateLabelToSayItsComplete();
}

public async void MyMethod()
{
  var result = Task.Factory.StartNew(DoSomethingThatTakesTime);
  await result;
  UpdateLabelToSayItsComplete();
}

private void DoSomethingThatTakesTime()
{
  Thread.Sleep(10000);
}

418voto

Eric Lippert Points 300275

J'ai peut-être raté quelque chose.

Vous l'êtes.

quelle est la différence entre faire Task.Wait y await task ?

Vous commandez votre déjeuner au serveur du restaurant. Un instant après avoir passé votre commande, un ami entre, s'assoit à côté de vous et entame une conversation. Vous avez alors deux choix. Vous pouvez ignorer votre ami jusqu'à ce que la tâche soit terminée - vous pouvez attendre que votre soupe arrive et ne rien faire d'autre pendant que vous attendez. Ou bien vous pouvez répondre à votre ami, et lorsque celui-ci cessera de parler, le serveur vous apportera votre soupe.

Task.Wait bloque jusqu'à ce que la tâche soit terminée -- vous ignorez votre ami jusqu'à ce que la tâche soit terminée. await continue de traiter les messages dans la file d'attente des messages, et lorsque la tâche est terminée, il met en file d'attente un message qui dit "reprenez là où vous en étiez après cette attente". Vous parlez à votre ami, et lorsqu'il y a une pause dans la conversation, la soupe arrive.

1 votes

N'est-ce pas ? Task.Wait un bloc coopératif ? c'est-à-dire qu'il ne bloquera pas le thread mais choisira une autre tâche dans le pool de tâches et l'exécutera pendant que le délai est encore actif.

6 votes

@ronag Non, ça ne l'est pas. Que diriez-vous si vous attendiez un Task qui prend 10 ms exécuterait en fait une opération de 10 heures. Task sur votre fil, vous bloquant ainsi pour les 10 heures entières ?

0 votes

@Eric Lippert ... alors voici ma principale préoccupation. await continue à traiter les messages dans la file d'attente des messages et lorsque la tâche est terminée .... alors cela signifie-t-il que await crée un autre thread et lui délègue sa tâche et pendant ce temps continue à traiter les messages dans la file d'attente des messages ? Mais beaucoup disent "await ne crée pas de nouveau thread" ? Alors qui exécute la tâche ?

126voto

Jon Points 9367

Pour démontrer la réponse d'Eric, voici un peu de code :

public void ButtonClick(object sender, EventArgs e)
{
  Task t = new Task.Factory.StartNew(DoSomethingThatTakesTime);
  t.Wait();  
  //If you press Button2 now you won't see anything in the console 
  //until this task is complete and then the label will be updated!
  UpdateLabelToSayItsComplete();
}

public async void ButtonClick(object sender, EventArgs e)
{
  var result = Task.Factory.StartNew(DoSomethingThatTakesTime);
  await result;
  //If you press Button2 now you will see stuff in the console and 
  //when the long method returns it will update the label!
  UpdateLabelToSayItsComplete();
}

public void Button_2_Click(object sender, EventArgs e)
{
  Console.WriteLine("Button 2 Clicked");
}

private void DoSomethingThatTakesTime()
{
  Thread.Sleep(10000);
}

30 votes

+1 pour le code (il est préférable d'exécuter une fois que de lire cent fois). Mais la phrase " //If you press Button2 now you won't see anything in the console until this task is complete and then the label will be updated! "est trompeur. En appuyant sur le bouton avec t.Wait(); dans le gestionnaire d'événement de clic de bouton ButtonClick() il n'est pas possible d'appuyer sur quoi que ce soit et de voir ensuite quelque chose dans la console et la mise à jour de l'étiquette "jusqu'à ce que cette tâche soit terminée" puisque l'interface graphique est gelée et ne répond pas, c'est-à-dire que tous les clics ou interactions avec l'interface graphique sont PERDU jusqu'à l'achèvement de la tâche en attente

2 votes

Je suppose qu'Eric part du principe que vous avez une connaissance de base de l'API Task. Je regarde ce code et je me dis "yup t.Wait va bloquer sur le fil principal jusqu'à ce que la tâche soit terminée."

54voto

Mas Points 1529

Cet exemple montre très clairement la différence. Avec async/await, le thread appelant ne se bloque pas et poursuit son exécution.

static void Main(string[] args)
{
    WriteOutput("Program Begin");
    // DoAsTask();
    DoAsAsync();
    WriteOutput("Program End");
    Console.ReadLine();
}

static void DoAsTask()
{
    WriteOutput("1 - Starting");
    var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime);
    WriteOutput("2 - Task started");
    t.Wait();
    WriteOutput("3 - Task completed with result: " + t.Result);
}

static async Task DoAsAsync()
{
    WriteOutput("1 - Starting");
    var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime);
    WriteOutput("2 - Task started");
    var result = await t;
    WriteOutput("3 - Task completed with result: " + result);
}

static int DoSomethingThatTakesTime()
{
    WriteOutput("A - Started something");
    Thread.Sleep(1000);
    WriteOutput("B - Completed something");
    return 123;
}

static void WriteOutput(string message)
{
    Console.WriteLine("[{0}] {1}", Thread.CurrentThread.ManagedThreadId, message);
}

Sortie DoAsTask :

\[1\] Program Begin
\[1\] 1 - Starting
\[1\] 2 - Task started
\[3\] A - Started something
\[3\] B - Completed something
\[1\] 3 - Task completed with result: 123
\[1\] Program End

Sortie DoAsAsync :

\[1\] Program Begin
\[1\] 1 - Starting
\[1\] 2 - Task started
\[3\] A - Started something
\[1\] Program End
\[3\] B - Completed something
\[3\] 3 - Task completed with result: 123

Mise à jour : Amélioration de l'exemple en montrant l'ID du fil dans la sortie.

14voto

Teoman shipahi Points 7988

Wait(), fera en sorte d'exécuter du code potentiellement asynchrone de manière synchrone. await ne le fera pas.

Par exemple, vous avez une application web asp.net. UserA appelle le point de terminaison /getUser/1. Le pool d'applications asp.net choisira un thread du pool de threads (Thread1) et, ce thread fera un appel http. Si vous faites Wait(), ce thread sera bloqué jusqu'à ce que l'appel http soit résolu. Pendant qu'il attend, si l'utilisateur B appelle /getUser/2, alors le pool d'applications devra servir un autre thread (Thread2) pour effectuer un nouvel appel http. Vous venez de créer (ou plutôt d'aller chercher dans le pool d'applications) un autre thread sans raison, parce que vous ne pouvez pas utiliser Thread1 qui a été bloqué par Wait().

Si vous utilisez await sur Thread1, alors, SyncContext gérera la synchronisation entre Thread1 et l'appel http. Simplement, il notifiera une fois l'appel http terminé. Pendant ce temps, si UserB appelle /getUser/2, alors, vous utiliserez à nouveau Thread1 pour faire l'appel http, parce qu'il a été libéré une fois que await a été atteint. Ensuite, une autre requête peut l'utiliser, encore plus. Une fois l'appel http effectué (user1 ou user2), Thread1 peut obtenir le résultat et retourner à l'appelant (client). Thread1 a été utilisé pour plusieurs tâches.

10voto

foson Points 5308

Dans cet exemple, pas grand-chose, en pratique. Si vous attendez une tâche qui revient sur un thread différent (comme un appel WCF) ou qui abandonne le contrôle au système d'exploitation (comme File IO), await utilisera moins de ressources système en ne bloquant pas un thread.

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