web-dev-qa-db-fra.com

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

Il se peut que je manque 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);
}
203
Jon

Il se peut que je manque quelque chose

Tu es.

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

Vous commandez votre déjeuner du serveur au restaurant. Un moment après avoir donné votre commande, un ami entre et s'assoit à côté de vous et entame une conversation. Maintenant vous avez 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 vous pouvez répondre à votre ami, et lorsque votre ami cesse 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, une fois la tâche terminée, il met en file d'attente un message qui indique "reprendre là où vous l'avez laissé après cette attente". Vous parlez à votre ami et quand il y a une pause dans la conversation, la soupe arrive.

385
Eric Lippert

Pour démontrer la réponse d'Eric, voici un 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);
}
117
Jon

Cet exemple montre très clairement la différence. Avec async/wait, le thread appelant ne se bloque pas et ne continue pas 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] Début du programme 
 [1] 1 - Démarrer 
 [1] 2 - Tâche démarrée 
 [3] A - Commencer quelque chose 
 [3] B - Quelque chose est terminé 
 [1] 3 - Tâche terminée avec résultat: 123 
 [1] Fin du programme 

Sortie DoAsAsync:

 [1] Début du programme 
 [1] 1 - Démarrer 
 [1] 2 - Tâche démarrée 
 [3] A - Commencer quelque chose 
 [1] Fin du programme 
 [3] B - Complété quelque chose 
 [3] 3 - Tâche terminée avec résultat: 123 

Mise à jour: exemple amélioré en affichant l'ID du thread dans la sortie.

45
Mas

Wait (), fera exécuter le code potentiellement asynchrone de manière synchrone. attendre ne sera pas.

Par exemple, vous avez une application Web asp.net. UserA appelle/getUser/1 point de terminaison. asp.net app pool choisira un thread dans le pool de threads (Thread1) et ce thread effectuera un appel http. Si vous attendez (), ce fil sera bloqué jusqu'à la résolution de l'appel http. Pendant qu'il attend, si UserB appelle/getUser/2, le pool d'applications devra servir un autre thread (Thread2) pour effectuer à nouveau un appel http. Vous venez de créer (en fait, à partir du pool d'applications) un autre thread sans aucune raison, car vous ne pouvez pas utiliser Thread1, il a été bloqué par Wait ().

Si vous utilisez wait sur Thread1, SyncContext gérera la synchronisation entre Thread1 et les appels http. Simplement, il notifiera une fois que l'appel http est terminé. En attendant, si UserB appelle/getUser/2, vous utiliserez à nouveau Thread1 pour établir un appel http, car il a été publié une fois que l'attente a été touchée. Ensuite, une autre demande peut l'utiliser, et même plus. Une fois l'appel http terminé (utilisateur1 ou utilisateur2), Thread1 peut obtenir le résultat et revenir à l'appelant (client). Thread1 a été utilisé pour plusieurs tâches.

9
Teoman shipahi

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

9
foson

Dans l'exemple ci-dessus, vous pouvez utiliser "TaskCreationOptions.HideScheduler" et modifier considérablement la méthode "DoAsTask". La méthode elle-même n'est pas asynchrone, comme c'est le cas avec "DoAsAsync", car elle renvoie une valeur "Task" et est marquée comme "async", ce qui permet de combiner plusieurs combinaisons. C'est ainsi qu'elle me donne exactement la même chose que d'utiliser "async/wait" :

static Task DoAsTask()
{
    WriteOutput("1 - Starting");
    var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime, TaskCreationOptions.HideScheduler); //<-- HideScheduler do the magic

    TaskCompletionSource<int> tsc = new TaskCompletionSource<int>();
    t.ContinueWith(tsk => tsc.TrySetResult(tsk.Result)); //<-- Set the result to the created Task

    WriteOutput("2 - Task started");

    tsc.Task.ContinueWith(tsk => WriteOutput("3 - Task completed with result: " + tsk.Result)); //<-- Complete the Task
    return tsc.Task;
}
3
user8545699