web-dev-qa-db-fra.com

Comment et quand utiliser "async" et "wait"

D'après ce que j'ai compris, l'une des principales choses que async ET await do est de rendre le code facile à écrire et à lire - mais leur utilisation équivaut-elle à créer des threads d'arrière-plan pour exécuter une logique de longue durée?

J'essaie actuellement l'exemple le plus fondamental. J'ai ajouté des commentaires en ligne. Pouvez-vous me le préciser?

// I don't understand why this method must be marked as `async`.
private async void button1_Click(object sender, EventArgs e)
{
    Task<int> access = DoSomethingAsync();
    // task independent stuff here

    // this line is reached after the 5 seconds sleep from 
    // DoSomethingAsync() method. Shouldn't it be reached immediately? 
    int a = 1; 

    // from my understanding the waiting should be done here.
    int x = await access; 
}

async Task<int> DoSomethingAsync()
{
    // is this executed on a background thread?
    System.Threading.Thread.Sleep(5000);
    return 1;
}
830
Dan Dinu

Lors de l'utilisation de async et await, le compilateur génère une machine à états en arrière-plan.

Voici un exemple sur lequel j'espère pouvoir expliquer certains des détails de haut niveau en cours: 

public async Task MyMethodAsync()
{
    Task<int> longRunningTask = LongRunningOperationAsync();
    // independent work which doesn't need the result of LongRunningOperationAsync can be done here

    //and now we call await on the task 
    int result = await longRunningTask;
    //use the result 
    Console.WriteLine(result);
}

public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation 
{
    await Task.Delay(1000); // 1 second delay
    return 1;
}

OK, alors qu'est-ce qui se passe ici:

  1. Task<int> longRunningTask = LongRunningOperationAsync(); commence à exécuter LongRunningOperation

  2. Le travail indépendant est effectué sur supposons que le fil principal (ID de fil = 1) alors await longRunningTask soit atteint. 

    Maintenant, si la longRunningTask n'a pas fini et qu'elle est toujours en cours d'exécution, MyMethodAsync() retournera à sa méthode d'appel, ainsi le thread principal ne sera pas bloqué. Lorsque longRunningTask est terminé, un thread du ThreadPool (n'importe quel thread) retournera à MyMethodAsync() dans son contexte précédent et poursuivra son exécution (dans ce cas, en imprimant le résultat sur la console). 

Un deuxième cas serait que la longRunningTask a déjà terminé son exécution et que le résultat est disponible. Lorsque nous avons atteint le await longRunningTask, nous avons déjà le résultat, de sorte que le code continue de s'exécuter sur le même thread. (dans ce cas, le résultat d'impression sur la console). Bien sûr, ce n'est pas le cas pour l'exemple ci-dessus, dans lequel un Task.Delay(1000) est impliqué. 

629
Dan Dinu

En plus des autres réponses, jetez un oeil à wait (Référence C #)

et plus précisément à l'exemple inclus, il explique un peu votre situation

L'exemple Windows Forms suivant illustre l'utilisation de wait dans un fichier méthode async, WaitAsynchronouslyAsync. Contrastez le comportement de cela méthode avec le comportement de WaitSynchronously. Sans attendre opérateur appliqué à une tâche, WaitSynchronously s'exécute de manière synchrone malgré l'utilisation du modificateur asynchrone dans sa définition et un appel à Thread.Sleep dans son corps.

private async void button1_Click(object sender, EventArgs e)
{
    // Call the method that runs asynchronously.
    string result = await WaitAsynchronouslyAsync();

    // Call the method that runs synchronously.
    //string result = await WaitSynchronously ();

    // Display the result.
    textBox1.Text += result;
}

// The following method runs asynchronously. The UI thread is not
// blocked during the delay. You can move or resize the Form1 window 
// while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
    await Task.Delay(10000);
    return "Finished";
}

// The following method runs synchronously, despite the use of async.
// You cannot move or resize the Form1 window while Thread.Sleep
// is running because the UI thread is blocked.
public async Task<string> WaitSynchronously()
{
    // Add a using directive for System.Threading.
    Thread.Sleep(10000);
    return "Finished";
}
142
Adriaan Stander

A ma connaissance, l’une des principales choses qu’attendent et attendent async est de rendre le code facile à écrire et à lire.

Ils doivent rendre le code asynchrone facile à écrire et à lire, oui.

Est-ce la même chose que de créer des threads d'arrière-plan pour effectuer une logique de longue durée?

Pas du tout.

// Je ne comprends pas pourquoi cette méthode doit être marquée comme "asynchrone".

Le mot clé async active le mot clé await. Donc, toute méthode utilisant await doit être marquée async.

// cette ligne est atteinte après les 5 secondes de veille de la méthode DoSomethingAsync (). Ne devrait-il pas être atteint immédiatement? 

Non, car les méthodes async ne sont pas exécutées sur un autre thread par défaut.

// est-ce exécuté sur un thread d'arrière-plan?

Non.


Vous pouvez trouver ma async/await intro utile. Les officiels MSDN docs sont aussi exceptionnellement bons (en particulier le TAP section), et l’équipe async a sorti un excellent FAQ .

122
Stephen Cleary

Explication

Voici un exemple rapide d'async/wait à un niveau élevé. Il y a beaucoup plus de détails à considérer au-delà de cela.

Remarque: Task.Delay(1000) simule un travail d'une seconde. Je pense qu'il est préférable de penser à cela comme à l'attente d'une réponse d'une ressource externe. Puisque notre code attend une réponse, le système peut définir la tâche en cours sur le côté et y revenir une fois terminée. En attendant, il peut faire un autre travail sur ce fil.

Dans l'exemple ci-dessous, le premier bloc fait exactement cela. Il commence immédiatement toutes les tâches (les lignes Task.Delay) et les met de côté. Le code s'interrompra sur la ligne await a jusqu'à ce que le délai d'une seconde soit écoulé avant de passer à la ligne suivante. Puisque b, c, d et e ont tous commencé à s'exécuter presque exactement au même moment que a (faute d'attendre), ils devraient se terminer à peu près au même moment dans ce cas.

Dans l'exemple ci-dessous, le bloc second commence une tâche et attend sa fin (c'est ce que fait await) avant de démarrer les tâches suivantes. Chaque itération prend 1 seconde. La await met le programme en pause et attend le résultat avant de continuer. C'est la principale différence entre les premier et deuxième blocs.

Exemple

Console.WriteLine(DateTime.Now);

// This block takes 1 second to run because all
// 5 tasks are running simultaneously
{
    var a = Task.Delay(1000);
    var b = Task.Delay(1000);
    var c = Task.Delay(1000);
    var d = Task.Delay(1000);
    var e = Task.Delay(1000);

    await a;
    await b;
    await c;
    await d;
    await e;
}

Console.WriteLine(DateTime.Now);

// This block takes 5 seconds to run because each "await"
// pauses the code until the task finishes
{
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
}
Console.WriteLine(DateTime.Now);

SORTIE:

5/24/2017 2:22:50 PM
5/24/2017 2:22:51 PM (First block took 1 second)
5/24/2017 2:22:56 PM (Second block took 5 seconds)

Informations supplémentaires concernant SynchronizationContext

Note: C'est ici que les choses deviennent un peu embuées, alors si je me trompe, corrigez-moi et je mettrai à jour la réponse. Il est important de bien comprendre comment cela fonctionne, mais vous pouvez vous en passer, sans être un expert, tant que vous n'utilisez jamais ConfigureAwait(false), même si vous risquez de perdre une occasion d'optimisation, je suppose.

Il y a un aspect de ceci qui rend le concept asynchrone/wait un peu plus difficile à comprendre. C'est le fait que dans cet exemple, tout cela se passe sur le même thread (ou du moins ce qui semble être le même thread en ce qui concerne son SynchronizationContext). Par défaut, await restaure le contexte de synchronisation du thread d'origine sur lequel il était exécuté. Par exemple, dans ASP.NET, vous avez un HttpContext qui est lié à un fil lorsqu'une requête arrive. Ce contexte contient des éléments spécifiques à la requête Http d'origine, tels que l'objet Request d'origine qui contient des éléments tels que le langage, l'adresse IP, les en-têtes, etc. etc. Si vous changez de thread à la moitié du traitement, vous pourriez éventuellement essayer d'extraire des informations de cet objet sur un autre HttpContext, ce qui pourrait être désastreux. Si vous savez que vous n'utiliserez pas le contexte pour quoi que ce soit, vous pouvez choisir de ne pas vous en soucier. Cela permet essentiellement à votre code de s'exécuter sur un thread séparé sans modifier le contexte.

Comment y parvenez-vous? Par défaut, le code await a; suppose que vous voulez capturer et restaurer le contexte:

await a; //Same as the line below
await a.ConfigureAwait(true);

Si vous souhaitez autoriser le code principal à continuer sur un nouveau thread sans le contexte d'origine, vous utilisez simplement false au lieu de true pour qu'il sache qu'il n'est pas nécessaire de restaurer le contexte.

await a.ConfigureAwait(false);

Une fois le programme en pause, il continuera potentiellement sur un thread totalement différent avec un contexte différent. C’est d’où l’amélioration des performances - elle pourrait continuer sur n’importe quel thread disponible sans avoir à restaurer le contexte original avec lequel elle avait commencé.

Est-ce que ça dérange? Enfer ouais! Pouvez-vous comprendre? Probablement! Une fois que vous avez compris les concepts, passez aux explications de Stephen Cleary, qui ont tendance à être davantage orientées vers une personne ayant déjà une compréhension technique de l'async/wait.

116
Joe Phillips

Afficher les explications ci-dessus en action dans un programme de console simple -

class Program
{
    static void Main(string[] args)
    {
        TestAsyncAwaitMethods();
        Console.WriteLine("Press any key to exit...");
        Console.ReadLine();
    }

    public async static void TestAsyncAwaitMethods()
    {
        await LongRunningMethod();
    }

    public static async Task<int> LongRunningMethod()
    {
        Console.WriteLine("Starting Long Running method...");
        await Task.Delay(5000);
        Console.WriteLine("End Long Running method...");
        return 1;
    }
}

Et le résultat est:

Starting Long Running method...
Press any key to exit...
End Long Running method...

Ainsi,

  1. Main lance la méthode d'exécution longue via TestAsyncAwaitMethods. Cela revient immédiatement sans arrêter le fil actuel et nous voyons immédiatement le message 'Appuyez sur n’importe quelle touche pour quitter'
  2. Pendant tout ce temps, LongRunningMethod s'exécute en arrière-plan. Une fois terminé, un autre thread de Threadpool prend ce contexte et affiche le message final.

Ainsi, pas de thread est bloqué. 

47
sppc42

Je pense que vous avez choisi un mauvais exemple avec System.Threading.Thread.Sleep

Le point d'une tâche async consiste à le laisser s'exécuter en arrière-plan sans verrouiller le thread principal, par exemple en effectuant une DownloadFileAsync

System.Threading.Thread.Sleep n'est pas "en cours", il dort juste, et donc votre ligne suivante est atteinte après 5 secondes ...

Lisez cet article, je pense que c’est une excellente explication des concepts async et await: http://msdn.Microsoft.com/en-us/library/vstudio/hh191443.aspx

38
Vnuk

Voici un rapide programme de console pour le rendre clair à ceux qui suivent. La méthode "TaskToDo" est votre méthode d'exécution longue que vous souhaitez rendre asynchrone. Le faire exécuter Async est fait par la méthode TestAsync. La méthode des boucles de test exécute simplement les tâches "TaskToDo" et les exécute en mode asynchrone. Vous pouvez voir cela dans les résultats, car ils ne se terminent pas dans le même ordre, ils rendent compte au thread de l'interface utilisateur de la console lorsqu'ils sont terminés. Simpliste, mais je pense que les exemples simplistes font mieux ressortir le noyau du modèle que des exemples plus complexes:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TestingAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            TestLoops();
            Console.Read();
        }

        private static async void TestLoops()
        {
            for (int i = 0; i < 100; i++)
            {
                await TestAsync(i);
            }
        }

        private static Task TestAsync(int i)
        {
            return Task.Run(() => TaskToDo(i));
        }

        private async static void TaskToDo(int i)
        {
            await Task.Delay(10);
            Console.WriteLine(i);
        }
    }
}
17
MarkWalls

Cette réponse vise à fournir des informations spécifiques à ASP.NET.

En utilisant async/wait dans le contrôleur MVC, il est possible d'augmenter l'utilisation du pool de threads et d'atteindre un débit nettement supérieur, comme expliqué dans l'article ci-dessous.

http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4

Dans les applications Web qui voient un grand nombre de demandes simultanées à démarrage ou a une charge éclatante (où la concurrence augmente soudainement), rendre ces appels de service Web asynchrones augmentera le réactivité de votre application. Une demande asynchrone prend le même durée de traitement qu'une demande synchrone. Par exemple, si une demande effectue un appel de service Web qui nécessite deux secondes à terminée, la demande prend deux secondes, qu’elle soit exécutée de manière synchrone ou asynchrone. Cependant, lors d'un appel asynchrone, un thread n'est pas empêché de répondre à d'autres demandes pendant qu'il attend la fin de la première demande. Par conséquent, asynchrone les demandes empêchent la mise en file d'attente des demandes et la croissance du pool de threads lorsqu'il existe de nombreuses demandes simultanées qui invoquent des opérations de longue durée.

11
Lex Li

Toutes les réponses ici utilisent Task.Delay () ou une autre fonction async intégrée. Mais voici mon exemple qui n'utilise aucune de ces fonctions asynchrones:

    // Starts counting to a large numbewr and then immediately displays message "i'm counting...". 
    // Then it waits for task to finish and displays "finished, press any key".
    static void asyncTest ()
    {
        Console.WriteLine("Started asyncTest()");
        Task<long> task = asyncTest_count();
        Console.WriteLine("Started counting, please wait...");
        task.Wait(); // if you comment this line you will see that message "Finished counting" will be displayed before we actually finished counting.
        //Console.WriteLine("Finished counting to " + task.Result.ToString()); // using task.Result seems to also call task.Wait().
        Console.WriteLine("Finished counting.");
        Console.WriteLine("Press any key to exit program.");
        Console.ReadLine();
    }

    static async Task<long> asyncTest_count()
    {
        long k = 0;
        Console.WriteLine("Started asyncTest_count()");
        await Task.Run(() =>
        {
            long countTo = 100000000;
            int prevPercentDone = -1;
            for (long i = 0; i <= countTo; i++)
            {
                int percentDone = (int)(100 * (i / (double)countTo));
                if (percentDone != prevPercentDone)
                {
                    prevPercentDone = percentDone;
                    Console.Write(percentDone.ToString() + "% ");
                }

                k = i;
            }
        });
        Console.WriteLine("");
        Console.WriteLine("Finished asyncTest_count()");
        return k;
    }
10
Tone Škoda

Pour être honnête, je pense toujours que la meilleure explication est celle concernant l'avenir et les promesses sur Wikipedia: http://en.wikipedia.org/wiki/Futures_and_promises

L'idée de base est que vous avez un pool séparé de threads qui exécutent les tâches de manière asynchrone. En l'utilisant. L'objet fait toutefois la promesse qu'il exécutera l'opération à un moment donné et vous donnera le résultat lorsque vous le demanderez. Cela signifie qu'il se bloquera lorsque vous demanderez le résultat et ne sera pas terminé, mais s'exécutera autrement dans le pool de threads.

À partir de là, vous pouvez optimiser les éléments: certaines opérations peuvent être implémentées de manière async et vous pouvez optimiser des éléments comme le fichier IO et la communication réseau en regroupant les demandes suivantes et/ou en les réorganisant. Je ne sais pas si cela fait déjà partie de la structure de tâches de Microsoft - mais si ce n'était pas le cas, ce serait l'une des premières choses que j'ajouterais.

Vous pouvez réellement mettre en œuvre le type de modèle futur avec des rendements en C # 4.0. Si vous voulez savoir comment cela fonctionne exactement, je peux recommander ce lien qui fait un travail correct: http://code.google.com/p/fracture/source/browse/trunk/Squared/TaskLib/ . Cependant, si vous commencez à vous amuser vous-même, vous remarquerez que vous avez vraiment besoin d'une prise en charge linguistique si vous voulez faire toutes les choses cool - c'est exactement ce que Microsoft a fait.

9
atlaste

Voir ce violon https://dotnetfiddle.net/VhZdLU (et l'améliorer si possible) pour exécuter une application console simple qui montre les utilisations de Task, Task.WaitAll (), async et attend opérateurs dans le même programme. 

Ce violon devrait effacer votre concept de cycle d'exécution. 

Voici l exemple de code

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {               
        var a = MyMethodAsync(); //Task started for Execution and immediately goes to Line 19 of the code. Cursor will come back as soon as await operator is met       
        Console.WriteLine("Cursor Moved to Next Line Without Waiting for MyMethodAsync() completion");
        Console.WriteLine("Now Waiting for Task to be Finished");       
        Task.WaitAll(a); //Now Waiting      
        Console.WriteLine("Exiting CommandLine");       
    }

    public static async Task MyMethodAsync()
    {
        Task<int> longRunningTask = LongRunningOperation();
        // independent work which doesn't need the result of LongRunningOperationAsync can be done here
        Console.WriteLine("Independent Works of now executes in MyMethodAsync()");
        //and now we call await on the task 
        int result = await longRunningTask;
        //use the result 
        Console.WriteLine("Result of LongRunningOperation() is " + result);
    }

    public static async Task<int> LongRunningOperation() // assume we return an int from this long running operation 
    {
        Console.WriteLine("LongRunningOperation() Started");
        await Task.Delay(2000); // 2 second delay
        Console.WriteLine("LongRunningOperation() Finished after 2 Seconds");
        return 1;
    }   

}

Trace provenant de la fenêtre de sortie: enter image description here

6
vibs2006
public static void Main(string[] args)
{
    string result = DownloadContentAsync().Result;
    Console.ReadKey();
}

// You use the async keyword to mark a method for asynchronous operations.
// The "async" modifier simply starts synchronously the current thread. 
// What it does is enable the method to be split into multiple pieces.
// The boundaries of these pieces are marked with the await keyword.
public static async Task<string> DownloadContentAsync()// By convention, the method name ends with "Async
{
    using (HttpClient client = new HttpClient())
    {
        // When you use the await keyword, the compiler generates the code that checks if the asynchronous operation is finished.
        // If it is already finished, the method continues to run synchronously.
        // If not completed, the state machine will connect a continuation method that must be executed WHEN the Task is completed.


        // Http request example. 
        // (In this example I can set the milliseconds after "sleep=")
        String result = await client.GetStringAsync("http://httpstat.us/200?sleep=1000");

        Console.WriteLine(result);

        // After completing the result response, the state machine will continue to synchronously execute the other processes.


        return result;
    }
}
3

Sur un niveau supérieur:

1) Le mot-clé asynchrone active l'attente et c'est tout ce qu'il fait. Le mot clé async n'exécute pas la méthode dans un thread séparé. La méthode asynchrone de début est exécutée de manière synchrone jusqu'à ce qu'elle obtienne une attente d'une tâche fastidieuse.

2) Vous pouvez attendre une méthode qui retourne une tâche ou une tâche de type T. Vous ne pouvez pas attendre une méthode void async.

3) Au moment où le thread principal rencontre une tâche fastidieuse ou au démarrage du travail, le thread principal revient à l'appelant de la méthode en cours. 

4) Si le thread principal voit attendre sur une tâche en cours d'exécution, il ne l'attend pas et retourne à l'appelant de la méthode en cours. De cette façon, l'application reste sensible.

5) La tâche de traitement en attente est maintenant exécutée sur un thread séparé du pool de threads.

6) Lorsque cette tâche en attente est terminée, tout le code ci-dessous sera exécuté par le thread séparé

Voici l'exemple de code. Exécutez-le et vérifiez l'id du thread

using System;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncAwaitDemo
{
    class Program
    {
        public static async void AsynchronousOperation()
        {
            Console.WriteLine("Inside AsynchronousOperation Before AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            //Task<int> _task = AsyncMethod();
            int count = await AsyncMethod();

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod Before Await, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            //int count = await _task;

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await Before DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            DependentMethod(count);

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await After DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
        }

        public static async Task<int> AsyncMethod()
        {
            Console.WriteLine("Inside AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            int count = 0;

            await Task.Run(() =>
            {
                Console.WriteLine("Executing a long running task which takes 10 seconds to complete, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(20000);
                count = 10;
            });

            Console.WriteLine("Completed AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            return count;
        }       

        public static void DependentMethod(int count)
        {
            Console.WriteLine("Inside DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId + ". Total count is " + count);
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Started Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            AsynchronousOperation();

            Console.WriteLine("Completed Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            Console.ReadKey();
        }

    }
}
3
ABajpai

Si j'ai bien compris, il faudrait ajouter un troisième terme à la composition: Task.

Async est simplement un qualificatif que vous mettez sur votre méthode pour dire que c'est une méthode asynchrone.

Task est le retour de la fonction async. Il s'exécute de manière asynchrone.

Vous await une tâche. Lorsque l'exécution du code atteint cette ligne, le contrôle revient à l'appelant de la fonction d'origine environnante.

Si, au lieu de cela, vous affectez le retour d'une fonction async (c'est-à-dire Task) à une variable, lorsque l'exécution du code atteint cette ligne, il ne fait que continuer après cette ligne dans la fonction environnante alors que la Task est exécutée de manière asynchrone.

2
user21306

Dans le code suivant, la méthode HttpClient GetByteArrayAsync renvoie une tâche, getContentsTask. La tâche est une promesse de produire le tableau d'octets réel une fois la tâche terminée. L'opérateur wait est appliqué à getContentsTask pour suspendre l'exécution dans SumPageSizesAsync jusqu'à ce que getContentsTask soit terminé. Pendant ce temps, le contrôle est retourné à l'appelant de SumPageSizesAsync. Lorsque getContentsTask est terminé, l'expression wait se transforme en un tableau d'octets.

private async Task SumPageSizesAsync()
{
    // To use the HttpClient type in desktop apps, you must include a using directive and add a 
    // reference for the System.Net.Http namespace.
    HttpClient client = new HttpClient();
    // . . .
    Task<byte[]> getContentsTask = client.GetByteArrayAsync(url);
    byte[] urlContents = await getContentsTask;

    // Equivalently, now that you see how it works, you can write the same thing in a single line.
    //byte[] urlContents = await client.GetByteArrayAsync(url);
    // . . .
}
1
lazydeveloper

Async/attendre

En fait, Async/Await sont une paire de mots-clés qui ne sont que du sucre syntaxique pour la création d'un rappel d'une tâche asynchrone.

Prenons par exemple cette opération:

    public static void DoSomeWork()
    {
        var task = Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS NOT bubbling up due to the different threads
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // This is the callback
        task.ContinueWith((t) => {
            // -> Exception is swallowed silently
            Console.WriteLine("Completed");

            // [RUNS ON WORKER THREAD]
        });
    }

Cette méthode présente plusieurs inconvénients. Les erreurs ne sont pas transmises et il est extrêmement difficile à lire . Mais Async et Await entrent en jeu pour nous aider:

    public async static void DoSomeWork()
    {
        var result = await Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS bubbling up
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // every thing below is a callback 
        // (including the calling methods)

        Console.WriteLine("Completed");

    }

Les appels en attente doivent être en méthodes asynchrones. Cela présente des avantages:

  • Renvoie le résultat de la tâche
  • crée automatiquement un rappel
  • vérifie les erreurs et les laisse bouillir dans le callstack (seulement les appels non attendus dans le callstack)
  • attend le résultat
  • libère le fil principal
  • exécute le rappel sur le thread principal
  • utilise un thread de travail du pool de threads pour la tâche
  • rend le code facile à lire
  • et beaucoup plus

NOTE: Async et Await sont utilisés avec appels asynchrones pas pour les créer. Vous devez utiliser Task Libary pour cela, comme Task.Run ().

Voici une comparaison entre les solutions wait et none wait

C'est la solution non asynchrone:

    public static long DoTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]
        var task = Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        Thread.Sleep(1000);
        // goes directly further
        // WITHOUT waiting until the task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 50 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

C'est la méthode asynchrone:

    public async static Task<long> DoAwaitTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]

        await Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // Waits until task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 2050 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

Vous pouvez réellement appeler une méthode asynchrone sans le mot-clé wait, mais cela signifie que toutes les exceptions ici sont avalées en mode de publication:

    public static Stopwatch stopWatch { get; } = new Stopwatch();

    static void Main(string[] args)
    {
        Console.WriteLine("DoAwaitTask: " + DoAwaitTask().Result + " ms");
        // 2050 (2000 more because of the await)
        Console.WriteLine("DoTask: " + DoTask() + " ms");
        // 50
        Console.ReadKey();
    }

Async et Await ne sont pas conçus pour l'informatique parallèle. Ils sont utilisés pour ne pas bloquer votre thread principal. S'il s'agit d'asp.net ou d'applications Windows. Bloquer votre thread principal en raison d'un appel réseau est une mauvaise chose. Si vous faites cela, votre application ne répondra plus ou risque de se bloquer.

Consultez ms docs pour obtenir des exemples.

1
Hakim

est de les utiliser égal à créer des threads d'arrière-plan pour effectuer long logique de durée?

Cet article MDSN: Programmation asynchrone avec async et wait (C #) l'explique explicitement:

Les mots-clés async et wait n'entraînent pas la création de threads supplémentaires créé. Les méthodes asynchrones ne nécessitent pas de multithreading, car un asynchrone La méthode ne fonctionne pas sur son propre thread. La méthode s'exécute sur le courant contexte de synchronisation et utilise le temps sur le fil uniquement lorsque le la méthode est active.

1
Dmitry G.

Voici les règles du pouce que j'utilise pour décider quand utiliser les promesses et quand utiliser async

  1. La fonction asynchrone renvoie une promesse. L'inverse est également vrai. Toute fonction qui retourne une promesse peut être considérée comme une fonction async. Wait est utilisée pour appeler une fonction async et attend qu'elle soit résolue ou rejetée.

  2. wait bloque l'exécution du code dans la fonction asynchrone dans laquelle il se trouve. Si la sortie de function2 dépend de la sortie de function1, j'utilise wait.

  3. Si deux fonctions peuvent être exécutées en parallèle, créez deux fonctions asynchrones différentes, puis exécutez-les en parallèle.

  4. Pour exécuter des promesses en parallèle, créez un tableau de promesses, puis utilisez Promise.all (promisesArray). Chaque fois que vous l'utilisez, rappelez-vous que vous écrivez du code bloquant. Au fil du temps, nous avons tendance à négliger cela.

  5. Au lieu de créer d’énormes fonctions asynchrones avec beaucoup d’attentes dans asyncFunction (), il est préférable de créer des fonctions asynchrones plus petites. De cette façon, nous serons conscients de ne pas écrire trop de code bloquant. L’utilisation de fonctions asynchrones plus petites présente un autre avantage: vous vous obligez à définir les fonctions asynchrones pouvant être exécutées en parallèle.

  6. Si votre code contient du code bloquant, il est préférable d’en faire une fonction asynchrone. En faisant cela, vous vous assurez que quelqu'un d'autre peut utiliser votre fonction de manière asynchrone.

  7. En rendant les fonctions asynchrones hors du code bloquant, vous permettez à l'utilisateur qui appellera votre fonction de décider du niveau d'asynhronique qu'il souhaite.

J'espère que cela vous aide à décider facilement quand utiliser les promesses et quand utiliser les promesses et quand utiliser

0
Sunny Sultan

Ci-dessous, le code qui lit le fichier Excel en ouvrant une boîte de dialogue, puis utilise asynchrone et attend pour exécuter de manière asynchrone le code qui lit une ligne à la fois à partir d’Excel et se lie à la grille. 

namespace EmailBillingRates
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            lblProcessing.Text = "";
        }

        private async void btnReadExcel_Click(object sender, EventArgs e)
        {
            string filename = OpenFileDialog();

            Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
            Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(filename);
            Microsoft.Office.Interop.Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
            Microsoft.Office.Interop.Excel.Range xlRange = xlWorksheet.UsedRange;
            try
            {
                Task<int> longRunningTask = BindGrid(xlRange);
                int result = await longRunningTask;

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
            }
            finally
            {
                //cleanup  
               // GC.Collect();
                //GC.WaitForPendingFinalizers();

                //rule of thumb for releasing com objects:  
                //  never use two dots, all COM objects must be referenced and released individually  
                //  ex: [somthing].[something].[something] is bad  

                //release com objects to fully kill Excel process from running in the background  
                Marshal.ReleaseComObject(xlRange);
                Marshal.ReleaseComObject(xlWorksheet);

                //close and release  
                xlWorkbook.Close();
                Marshal.ReleaseComObject(xlWorkbook);

                //quit and release  
                xlApp.Quit();
                Marshal.ReleaseComObject(xlApp);
            }

        }

        private void btnSendEmail_Click(object sender, EventArgs e)
        {

        }

        private string OpenFileDialog()
        {
            string filename = "";
            OpenFileDialog fdlg = new OpenFileDialog();
            fdlg.Title = "Excel File Dialog";
            fdlg.InitialDirectory = @"c:\";
            fdlg.Filter = "All files (*.*)|*.*|All files (*.*)|*.*";
            fdlg.FilterIndex = 2;
            fdlg.RestoreDirectory = true;
            if (fdlg.ShowDialog() == DialogResult.OK)
            {
                filename = fdlg.FileName;
            }
            return filename;
        }

        private async Task<int> BindGrid(Microsoft.Office.Interop.Excel.Range xlRange)
        {
            lblProcessing.Text = "Processing File.. Please wait";
            int rowCount = xlRange.Rows.Count;
            int colCount = xlRange.Columns.Count;

            // dt.Column = colCount;  
            dataGridView1.ColumnCount = colCount;
            dataGridView1.RowCount = rowCount;

            for (int i = 1; i <= rowCount; i++)
            {
                for (int j = 1; j <= colCount; j++)
                {
                    //write the value to the Grid  
                    if (xlRange.Cells[i, j] != null && xlRange.Cells[i, j].Value2 != null)
                    {
                         await Task.Delay(1);
                         dataGridView1.Rows[i - 1].Cells[j - 1].Value =  xlRange.Cells[i, j].Value2.ToString();
                    }

                }
            }
            lblProcessing.Text = "";
            return 0;
        }
    }

    internal class async
    {
    }
}
0
Zaheer Abbas

Les réponses ici sont utiles en tant que conseils généraux sur wait/async. Ils contiennent également des détails sur la manière dont wait/async est câblé. J'aimerais partager avec vous une expérience pratique que vous devriez connaître avant d'utiliser ce modèle.

Le terme "wait" est littéral, donc quel que soit le thread sur lequel vous l'appelez, il attendra le résultat de la méthode avant de continuer. Sur le fil foreground, il s'agit d'un catastrophe. Le fil de premier plan est chargé de la construction de votre application, notamment des vues, des modèles de vues, des animations initiales et de tout ce que vous avez amorcé avec ces éléments. Ainsi, lorsque vous attendez le fil de premier plan, vous arrêtez l'application. L'utilisateur attend et attend quand rien ne semble se passer. Cela fournit une expérience utilisateur négative.

Vous pouvez certainement attendre un fil de fond en utilisant une variété de moyens:

Device.BeginInvokeOnMainThread(async () => { await AnyAwaitableMethod(); });

// Notice that we do not await the following call, 
// as that would tie it to the foreground thread.
try
{
Task.Run(async () => { await AnyAwaitableMethod(); });
}
catch
{}

Le code complet de ces remarques se trouve à https://github.com/marcusts/xamarin-forms-annoyances . Voir la solution appelée AwaitAsyncAntipattern.sln.

Le site GitHub fournit également des liens vers une discussion plus détaillée sur ce sujet.

0
Stephen Marcus