web-dev-qa-db-fra.com

À quoi sert la nouvelle fonctionnalité d'attente C #?

Quelqu'un peut-il expliquer ce que fait la fonction await?

82
Chris Nicol

Ils ont juste parlé de cela au PDC hier!

Await est utilisé en conjonction avec des tâches (programmation parallèle) dans .NET. Il s'agit d'un mot clé introduit dans la prochaine version de .NET. Il vous permet plus ou moins de "suspendre" l'exécution d'une méthode pour attendre la fin de l'exécution de la tâche. Voici un bref exemple:

//create and run a new task  
Task<DataTable> dataTask = new Task<DataTable>(SomeCrazyDatabaseOperation);

//run some other code immediately after this task is started and running  
ShowLoaderControl();  
StartStoryboard();

//this will actually "pause" the code execution until the task completes.  It doesn't lock the thread, but rather waits for the result, similar to an async callback  
// please so also note, that the task needs to be started before it can be awaited. Otherwise it will never return
dataTask.Start();
DataTable table = await dataTask;

//Now we can perform operations on the Task result, as if we're executing code after the async operation completed  
listBoxControl.DataContext = table;  
StopStoryboard();  
HideLoaderControl();
61
RTigger

Fondamentalement, les mots clés async et await vous permettent de spécifier que l'exécution d'une méthode doit s'arrêter à toutes les utilisations de await, qui marquent les appels de méthode asynchrones, puis reprendre une fois la l'opération asynchrone est terminée. Cela vous permet d'appeler une méthode dans le thread principal d'une application et de gérer le travail complexe de manière asynchrone, sans avoir besoin de définir explicitement les threads et les jointures ou de bloquer le thread principal de l'application.

Considérez-le comme étant quelque peu similaire à un yield return instruction dans une méthode produisant un IEnumerable. Lorsque le runtime atteint yield, il enregistre essentiellement l'état actuel de la méthode et renvoie la valeur ou la référence produite. La prochaine fois que IEnumerator.MoveNext () est appelé sur l'objet de retour (qui est généré en interne par le runtime), l'ancien état de la méthode est restauré dans la pile et l'exécution continue avec la ligne suivante après le yield return comme si nous n'avions jamais quitté la méthode. Sans ce mot clé, un type IEnumerator doit être personnalisé pour stocker l'état et gérer les demandes d'itération, avec des méthodes qui peuvent devenir TRÈS complexes.

De même, une méthode marquée comme async doit avoir au moins un await. Sur un await, le runtime enregistrera l'état du thread actuel et la pile d'appels, effectuera l'appel asynchrone et se détendra dans la boucle de message du runtime pour gérer le message suivant et garder l'application réactive. Une fois l'opération asynchrone terminée, à la prochaine opportunité de planification, la pile d'appels pour l'opération asynchrone est repoussée et poursuivie comme si l'appel était synchrone.

Ainsi, ces deux nouveaux mots clés simplifient fondamentalement le codage des processus asynchrones, un peu comme yield return simplifie la génération d'énumérations personnalisées. Avec quelques mots clés et quelques connaissances de base, vous pouvez ignorer tous les détails confus et souvent sujets aux erreurs d'un modèle asynchrone traditionnel. Cela sera INVALUABLE dans à peu près n'importe quelle application GUI événementielle comme Winforms, WPF de Silverlight.

47
KeithS

La réponse actuellement acceptée est trompeuse. await ne met rien en pause. Tout d'abord, il ne peut être utilisé que dans des méthodes ou des lambdas marqués comme async et renvoyant Task ou void si vous ne vous souciez pas d'avoir une instance de Task en cours d'exécution dans cette méthode.

En voici une illustration:

internal class Program
{
    private static void Main(string[] args)
    {
        var task = DoWork();
        Console.WriteLine("Task status: " + task.Status);
        Console.WriteLine("Waiting for ENTER");
        Console.ReadLine();
    }

    private static async Task DoWork()
    {
        Console.WriteLine("Entered DoWork(). Sleeping 3");
        // imitating time consuming code
        // in a real-world app this should be inside task, 
        // so method returns fast
        Thread.Sleep(3000);

        await Task.Run(() =>
            {
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine("async task iteration " + i);
                    // imitating time consuming code
                    Thread.Sleep(1000);
                }
            });

        Console.WriteLine("Exiting DoWork()");
    }
}

Sortie:

Entré DoWork (). Dormir 3
itération de tâche asynchrone 0
État de la tâche: WaitingForActivation
En attente de ENTER
itération de tâche asynchrone 1
itération de tâche asynchrone 2
itération de tâche asynchrone 3
itération de tâche asynchrone 4
itération de tâche asynchrone 5
itération de tâche asynchrone 6
itération de tâche asynchrone 7
itération de tâche asynchrone 8
itération de tâche asynchrone 9
Quitter DoWork ()

31
Anri

Pour toute personne novice en programmation asynchrone dans .NET, voici une (totalement fausse) analogie dans un scénario que vous connaissez peut-être mieux - AJAX appels utilisant JavaScript/jQuery. Un simple jQuery AJAX post ressemble à ceci:

$.post(url, values, function(data) {
  // AJAX call completed, do something with returned data here
});

La raison pour laquelle nous traitons les résultats dans une fonction de rappel est que nous ne bloquons pas le thread actuel en attendant le retour de l'appel AJAX. Ce n'est que lorsque la réponse est prête que le rappel est déclenché, libérer le thread actuel pour faire d'autres choses en attendant.

Maintenant, si JavaScript prend en charge le mot clé await (ce qui bien sûr n'est pas le cas ( encore! )), vous pouvez obtenir la même chose avec ceci:

var data = await $.post(url, values);
// AJAX call completed, do something with returned data here

C'est beaucoup plus propre, mais il semble que nous ayons introduit un code de blocage synchrone. Mais le (faux) compilateur JavaScript aurait tout pris après await et l'a câblé dans un rappel, donc au moment de l'exécution, le deuxième exemple se comporterait comme le premier.

Il ne semble pas que cela vous fasse économiser beaucoup de travail, mais quand il s'agit de choses comme la gestion des exceptions et les contextes de synchronisation, le compilateur fait en fait beaucoup de soulever des charges lourdes pour vous. Pour en savoir plus, je recommanderais FAQ suivi de série de blogs de Stephen Cleary .

10
Todd Menier