Comment et quand utiliser "async" et "attendre"

1066

D'après ma compréhension, l'une des principales choses à faire asyncet àawait faire est de rendre le code facile à écrire et à lire - mais les utilise-t-il comme des threads d'arrière-plan pour générer une logique de longue durée?

J'essaie actuellement l'exemple le plus basique. J'ai ajouté quelques commentaires en ligne. Pouvez-vous le clarifier pour moi?

// 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;
}
Dan Dinu
la source
48
En outre, dans votre exemple, notez que vous obtenez un avertissement lorsque vous compilez le code ci-dessus. Faites attention à l'avertissement . Cela vous dit que ce code n'a pas de sens.
Eric Lippert

Réponses:

759

Lors de l'utilisation asyncet awaitle compilateur génère une machine d'état en arrière-plan.

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

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. Un travail indépendant est effectué sur supposons que le thread principal (Thread ID = 1) await longRunningTaskest alors atteint.

    Maintenant, si le longRunningTaskn'est pas terminé et qu'il est toujours en cours d'exécution, MyMethodAsync()reviendra à sa méthode d'appel, donc le thread principal ne sera pas bloqué. Une fois l'opération longRunningTaskterminée, un thread du ThreadPool (peut être n'importe quel thread) reviendra MyMethodAsync()dans son contexte précédent et poursuivra l'exécution (dans ce cas, l'impression du résultat sur la console).

Un deuxième cas serait que le longRunningTaska déjà terminé son exécution et que le résultat est disponible. En atteignant le, await longRunningTasknous avons déjà le résultat, donc le code continuera à s'exécuter sur le même thread. (dans ce cas, impression du résultat sur la console). Bien sûr, ce n'est pas le cas pour l'exemple ci-dessus, où il y a un Task.Delay(1000)impliqué.

Dan Dinu
la source
65
Pourquoi avons-nous un "attente" avec le "Task.Delay (1000);" dans la méthode asynchrone LongRunningOperation?
Benison Sam
3
@codea Dans les commentaires d'Eric Lippert à l'article, il a lié un article d'introduction à ce sujet où il compare spécifiquement la stratégie DoEvents avec async-wait
Camilo Martinez
13
Le fil @BenisonSam est un peu ancien, mais j'avais la même question et je cherchais une réponse. La raison de "l'attente" est que si nous omettons "l'attente", la LongRunningOperationAsync () reviendra immédiatement. En fait, le compilateur donnera un avertissement si nous supprimons l'attente. Le blog de Stephen Cleary blog.stephencleary.com/2011/09/… donne un excellent aperçu des discussions sur la conception.
shelbypereira
70
Si chaque méthode async doit avoir une attente à l'intérieur de celle-ci, et qu'une attente ne peut être effectuée que sur une méthode avec async, quand s'arrête-t-elle?
Bruno Santos
108
Cette réponse est clairement fausse. Ces nombreux votes positifs entraîneront une mauvaise compréhension pour de nombreux utilisateurs. La documentation MS indique clairement, aucun autre thread n'est utilisé lorsque vous utilisez simplement async, attendez. msdn.microsoft.com/en-us/library/mt674882.aspx Veuillez corriger la réponse. Pour cette raison, j'ai perdu une journée entière.
Krishna Deepak
171

D'après ma compréhension, l'une des principales choses que l'async et l'attente font 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 la génération de 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 'async'.

Le asyncmot clé active le awaitmot clé. Donc, toute méthode utilisant awaitdoit être marquée async.

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

Non, car les asyncméthodes 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 mon async/ awaitintro utile. Les documents MSDN officiels sont également exceptionnellement bons (en particulier la section TAP ), et l' asyncéquipe a publié une excellente FAQ .

Stephen Cleary
la source
6
Il ne fonctionne donc pas sur un thread d'arrière-plan, mais il ne bloque pas non plus. Cela est possible en raison des API asynchrones qui utilisent des rappels au lieu de jongler avec les threads. Vous lancez une opération (E / S, socket, ..) et recommencez à faire vos choses. Une fois l'opération terminée, le système d'exploitation appellera le rappel. C'est ce que fait Node.js ou le framework Python Twisted et ils ont aussi de belles explications.
Roman Plášil
3
"Le mot-clé async active le mot-clé wait. Donc, toute méthode utilisant wait doit être marquée async.", - mais pourquoi? cette réponse n'aide pas à comprendre pourquoi la méthode doit être marquée comme asynchrone. Le compilateur ne peut-il pas simplement déduire que la méthode est asynchrone en recherchant à l'intérieur des mots clés en attente?
Stanislav
9
@Stanislav: J'ai une entrée de blog qui répond à cette question.
Stephen Cleary
3
Précision suggérée: non, car les asyncméthodes ne sont pas exécutées sur un autre thread par défaut. Dans votre exemple, l' Sleep()appel à l'intérieur DoSomethingAsync()bloque le thread en cours, ce qui empêche l'exécution de se poursuivre à l'intérieur button1_Click()jusqu'à la DoSomethingAsync()fin. Notez que tandis que Thread.Sleep()bloque le thread d'exécution,Task.Delay() does not.
DavidRR
166

Explication

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

Remarque: Task.Delay(1000) simule le travail pendant 1 seconde. Je pense qu'il vaut mieux penser à cela comme attendant une réponse d'une ressource externe. Puisque notre code attend une réponse, le système peut mettre la tâche en cours de 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 démarre immédiatement toutes les tâches (les Task.Delaylignes) et les met de côté. Le code s'arrêtera sur la await aligne jusqu'à ce que le délai de 1 seconde soit effectué avant de passer à la ligne suivante. Depuis b, c, det etout a commencé l' exécution presque exactement en même temps que a( en raison du manque de await), ils devraient terminer à peu près en même temps dans ce cas.

Dans l'exemple ci-dessous, le deuxième bloc démarre une tâche et attend qu'elle se termine (c'est ce qui se awaitpasse) avant de démarrer les tâches suivantes. Chaque itération prend 1 seconde. leawait met le programme en pause et attend le résultat avant de continuer. Il s'agit de 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);

PRODUCTION:

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

Remarque: C'est là que les choses deviennent un peu brumeuses pour moi, donc si je me trompe, corrigez-moi et je mettrai à jour la réponse. Il est important d'avoir une compréhension de base de la façon dont cela fonctionne, mais vous pouvez vous en tirer sans être un expert tant que vous n'utilisez jamais ConfigureAwait(false), même si vous perdrez probablement une opportunité d'optimisation, je suppose.

Il y a un aspect de cela qui rend le concept async/ awaitquelque peu plus difficile à saisir. 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 restaurera le contexte de synchronisation du thread d'origine sur lequel il s'exécutait. Par exemple, dans ASP.NET, vous en avez un HttpContextqui est lié à un thread lorsqu'une demande arrive. Ce contexte contient des éléments spécifiques à la demande Http d'origine, tels que l'objet de demande d'origine qui contient des éléments tels que la langue, l'adresse IP, les en-têtes, etc. . Si vous changez de threads à mi-chemin du traitement de quelque chose, vous pourriez éventuellement essayer d'extraire des informations de cet objet sur un autreHttpContextce qui pourrait être désastreux. Si vous savez que vous n'utiliserez le contexte pour rien, vous pouvez choisir de "ne pas vous en soucier". Cela permet essentiellement à votre code de s'exécuter sur un thread séparé sans apporter le contexte avec lui.

Comment y parvenez-vous? Par défaut, le await a;code fait en réalité l'hypothèse 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'a pas besoin de restaurer le contexte.

await a.ConfigureAwait(false);

Une fois le programme en pause, il continuera potentiellement sur un thread entièrement différent avec un contexte différent. C'est de là que viendrait l'amélioration des performances - elle pourrait continuer sur n'importe quel thread disponible sans avoir à restaurer le contexte d'origine avec lequel elle a commencé.

Est-ce que c'est compliqué? Enfer ouais! Pouvez-vous le comprendre? Probablement! Une fois que vous avez compris les concepts, passez aux explications de Stephen Cleary qui tendent à être davantage orientées vers quelqu'un ayant une compréhension technique de async/ awaitdéjà.

Joe Phillips
la source
Disons si toutes ces tâches retournent un int et si j'utilise le résultat de la première tâche dans la deuxième tâche (ou un calcul) Ce serait faux?
veerendra gupta
3
@veerendragupta yes. Vous choisiriez consciemment de ne pas les exécuter de manière asynchrone dans ce cas (car ils ne sont pas asynchrones). Il y a aussi quelques autres choses à réaliser concernant le contexte de configuration que je n'entrerai pas ici
Joe Phillips
C'est donc await MethodCall()un gaspillage absolu? Vous pourriez aussi bien laisser tomber le await/ async?
Vitani
2
@Jocie Pas tout à fait. Lorsque vous appelez await, je pense qu'il libère le fil dans la piscine au lieu de le maintenir. Cela le rend disponible pour une utilisation ailleurs en attendant le retour de la tâche
Joe Phillips
2
@JoePhillips Je pense que ce que vous venez de dire est l'essence même de l'async / wait. Le thread appelant est libéré et peut être utilisé par d'autres processus sur la machine. Une fois l'appel en attente terminé, un nouveau thread est utilisé pour reprendre ce que l'appelant a commencé à l'origine. L'appelant attend toujours, mais l'avantage est qu'un thread est libéré entre-temps. C'est l'avantage de l'async / wait?
Bob Horn
147

Suite aux autres réponses, jetez un œil à l' attente (Référence C #)

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

L'exemple Windows Forms suivant illustre l'utilisation de wait dans une méthode asynchrone, WaitAsynchronouslyAsync. Comparez le comportement de cette méthode avec le comportement de WaitSynchronously. Sans opérateur d'attente appliqué à une tâche, WaitSynchronously s'exécute de manière synchrone malgré l'utilisation du modificateur async 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";
}
Adriaan Stander
la source
3
Merci d'avoir répondu. Mais WaitAsynchronouslyAsync () est-il exécuté sur un thread séparé?
Dan Dinu
32
Je le crois, dans la section Une expression d'attente ne bloque pas le thread sur lequel elle s'exécute. Au lieu de cela, il oblige le compilateur à inscrire le reste de la méthode async en tant que continuation de la tâche attendue. Le contrôle revient ensuite à l'appelant de la méthode async. Une fois la tâche terminée, elle invoque sa poursuite et l'exécution de la méthode async reprend là où elle s'était arrêtée.
Adriaan Stander
13
Selon cet article MSDN , «les mots clés asynchrones et en attente n'entraînent pas la création de threads supplémentaires .... une méthode async ne s'exécute pas sur son propre thread». Ma compréhension est qu'à l'attente des mots clés, le framework saute en avant (retour à l'appelant) pour permettre à tout le code indépendant possible de s'exécuter en attendant la fin des longues opérations. Je pense que cela signifie qu'une fois que tout le code indépendant a été exécuté, si la longue opération n'est pas revenue, il se bloquera. Mais j'apprends juste cela maintenant.
Vimes
9
@astander C'est incorrect. Il ne s'exécute pas sur un thread différent. Il s'agit simplement de planifier la suite (le reste de la méthode) à appeler lorsque le temporisateur utilisé par les Task.Delayincendies.
MgSam
1
Cette réponse est fausse à cause du sommeil. Voir la réponse acceptée avec attendre Task.Delay (1000); qui a le bon comportement.
Jared Updike
62

Affichage des 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 la sortie est:

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

Donc,

  1. Main démarre la méthode de longue durée via TestAsyncAwaitMethods . Cela revient immédiatement sans arrêter le thread actuel et nous voyons immédiatement le message `` Appuyez sur n'importe quelle touche pour quitter ''
  2. Pendant tout ce temps, le LongRunningMethodtourne en arrière-plan. Une fois terminé, un autre thread de Threadpool reprend ce contexte et affiche le message final

Ainsi, aucun thread n'est bloqué.

sppc42
la source
"Appuyez sur n'importe quelle touche pour quitter ..." s'affichera dans quelle partie de la sortie?
StudioX
1
et à quoi sert (retour 1)? Est-ce nécessaire?
StudioX
1
@StudioX je pense qu'il doit avoir un type de retour entier
Kuba Do
Je pense que la return 1partie mérite une explication supplémentaire: le awaitmot - clé vous permet de retourner Task<T>directement le type sous-jacent , ce qui facilite l'adaptation de votre code sortant au monde en attente / asynchrone . Mais vous n'êtes pas obligé de renvoyer une valeur, car il est possible de retourner un Tasksans spécifier de type de retour, ce qui serait l'équivalent d'une voidméthode synchrone . Gardez à l'esprit que C # autorise les async voidméthodes, mais vous devez éviter de le faire à moins que vous ne vous attaquiez aux gestionnaires d'événements.
Christiano Kiss
41

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

Le point d'une asynctâche est de la laisser s'exécuter en arrière-plan sans verrouiller le thread principal, comme faire unDownloadFileAsync

System.Threading.Thread.Sleep n'est pas quelque chose qui "se fait", il dort juste, et donc votre prochaine ligne est atteinte après 5 secondes ...

Lisez cet article, je pense que c'est une excellente explication asyncet awaitconcept: http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx

Vnuk
la source
3
Pourquoi Sleep est un mauvais exemple, mais Download est un bon exemple. C'est comme le genre de chose FooBar quand je vois Thread.Sleep Je comprends qu'il y a une tâche qui prend du temps. Je pense que sa question est pertinente
Abdurrahim
1
@Abdurrahim Thread.Sleepbloque le thread (le thread ne peut rien faire d'autre que rester inactif), mais pas une méthode asynchrone. Dans le cas de DownloadFileAsync, le thread peut aller faire autre chose jusqu'à ce qu'une réponse vienne du serveur distant. Un meilleur espace réservé pour «une tâche qui prend du temps» dans une méthode asynchrone est Task.Delay, car il s'agit en fait d'asynchrone.
Gabriel Luci
@GabrielLuci mon objection ne concerne pas le retard vs le sommeil; Votre réponse ressemble plus à une réponse paille; Si vous mettez cela en commentaire de la question, ce ne serait rien que je puisse objecter, mais en tant que réponse, ça sent plus comme une réponse de paille. Je pense qu'il est toujours bien d'utiliser async là même tous les appels qu'il doit faire seront les bloquer; Cela n'invalidera pas tout le but ... Même tout ce qui restera sera du sucre syntaxique, cela compte comme un cas valide,
Abdurrahim
1
Ce n'était pas ma réponse. Mais pour répondre à votre point: cela dépend du but de la méthode. S'il voulait juste une méthode à appeler, il a réussi. Mais dans ce cas, il essayait de créer une méthode qui s'exécute de manière asynchrone. Il l'a fait en utilisant simplement le asyncmot - clé. Mais sa méthode fonctionnait toujours de manière synchrone, et cette réponse expliquait parfaitement pourquoi: parce qu'il n'avait en fait exécuté aucun code asynchrone. Les méthodes marquées asyncfonctionnent toujours de manière synchrone jusqu'à ce que vous soyez awaitincomplet Task. S'il n'y en a pas await, la méthode s'exécute de manière synchrone et le compilateur vous en avertira.
Gabriel Luci
23

Voici un programme de console rapide pour le faire comprendre à ceux qui suivent. La TaskToDométhode est votre méthode de longue durée que vous souhaitez rendre asynchrone. Le faire fonctionner async se fait par la TestAsyncméthode. La méthode des boucles de test exécute simplement les TaskToDotâches et les exécute en mode asynchrone. Vous pouvez le voir dans les résultats, car ils ne se terminent pas dans le même ordre d'une exécution à l'autre - ils font rapport au thread de l'interface utilisateur de la console lorsqu'ils ont terminé. Simpliste, mais je pense que les exemples simplistes font mieux ressortir le cœur du modèle que les exemples plus impliqués:

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);
        }
    }
}
MarkWalls
la source
22

Pour un apprentissage plus rapide ..

  • Comprendre le flux d'exécution de la méthode (avec un diagramme): 3 minutes

  • Introspection de la question (pour apprendre): 1 min

  • Passez rapidement à travers le sucre de syntaxe: 5 minutes

  • Partagez la confusion d'un développeur: 5 minutes

  • Problème: changez rapidement une implémentation réelle du code normal en code asynchrone: 2 minutes

  • Où ensuite?

Comprendre le flux d'exécution de la méthode (avec un diagramme): 3 minutes

Dans cette image, concentrez-vous sur # 6 (rien de plus) entrez la description de l'image ici

À l'étape n ° 6: l'exécution s'est arrêtée ici car elle est à court de travail. Pour continuer, il a besoin d'un résultat de getStringTask (sorte de fonction). Par conséquent, il utilise un awaitopérateur pour suspendre sa progression et rendre le contrôle (rendement) à l'appelant (de cette méthode dans laquelle nous sommes). L'appel réel à getStringTask a été effectué plus tôt dans # 2. Au n ° 2, une promesse a été faite de renvoyer un résultat de chaîne. Mais quand rendra-t-il le résultat? Devrions-nous (# 1: AccessTheWebAsync) refaire un deuxième appel? Qui obtient le résultat, # 2 (instruction d'appel) ou # 6 (instruction en attente)

L'appelant externe d'AccessTheWebAsync () attend également maintenant. Ainsi, l'appelant attend AccessTheWebAsync et AccessTheWebAsync attend GetStringAsync pour le moment. La chose intéressante est AccessTheWebAsync a fait un peu de travail avant d'attendre (# 4) peut-être pour gagner du temps. La même liberté de multitâche est également disponible pour l'appelant externe (et tous les appelants de la chaîne) et c'est le plus grand plus de ce truc 'async'! Vous avez l'impression que c'est synchrone ... ou normal mais ce n'est pas le cas.

Rappelez-vous, la méthode a déjà été retournée (# 2), elle ne peut pas revenir à nouveau (pas de deuxième fois). Alors, comment l'appelant saura-t-il? Il s'agit de tâches! La tâche a été réussie. La tâche était attendue (pas la méthode, pas la valeur). La valeur sera définie dans la tâche. Le statut de la tâche sera défini pour se terminer. L'appelant surveille simplement la tâche (# 6). Donc 6 # est la réponse à l'endroit où / qui obtient le résultat. Lectures supplémentaires pour plus tard ici .

Questionner l'introspection pour apprendre le plaisir: 1 min

Ajustons un peu la question:

Comment et quand utiliser et ? asyncawait Tasks

Parce que l'apprentissage Taskcouvre automatiquement les deux autres (et répond à votre question)

Passez rapidement à travers le sucre de syntaxe: 5 minutes

  • Avant la conversion (méthode originale)

    internal static int Method(int arg0, int arg1) { int result = arg0 + arg1; IO(); // Do some long running IO. return result; }

  • une méthode Task-ified pour appeler la méthode ci-dessus

    internal static Task<int> MethodTask(int arg0, int arg1) { Task<int> task = new Task<int>(() => Method(arg0, arg1)); task.Start(); // Hot task (started task) should always be returned. return task; }

Avons-nous mentionné attendre ou async? Non. Appelez la méthode ci-dessus et vous obtenez une tâche que vous pouvez surveiller. Vous savez déjà ce que la tâche renvoie .. un entier.

  • L'appel d'une tâche est légèrement délicat et c'est à ce moment que les mots clés commencent à apparaître. Appelons MethodTask ()

    internal static async Task<int> MethodAsync(int arg0, int arg1) { int result = await HelperMethods.MethodTask(arg0, arg1); return result; }

Même code ci-dessus ajouté comme image ci-dessous: entrez la description de l'image ici

  1. Nous attendons que la tâche soit terminée. D'où leawait
  2. Puisque nous utilisons wait, nous devons utiliser async(syntaxe obligatoire)
  3. MethodAsync avec Asynccomme préfixe (norme de codage)

awaitest facile à comprendre mais les deux ( async, Async) restants peuvent ne pas l'être :). Eh bien, il faut faire beaucoup plus de sens au compilateur though.Further lit pour plus tard ici

Il y a donc 2 parties.

  1. Créer une «tâche»
  2. Créer du sucre syntaxique pour appeler la tâche ( await+async)

Rappelez-vous, nous avons eu un appelant externe à AccessTheWebAsync () et cet appelant n'est pas épargné non plus ... c'est-à-dire qu'il a également besoin du même await+async. Et la chaîne continue. Mais il y aura toujours un Taskà une extrémité.

Tout va bien, mais un développeur a été surpris de voir # 1 (tâche) manquant ...

Partagez la confusion d'un développeur: 5 minutes

Un développeur a fait une erreur de ne pas implémenter Taskmais cela fonctionne toujours! Essayez de comprendre la question et juste la réponse acceptée fournie ici . J'espère que vous avez lu et bien compris. Le résumé est que nous ne pouvons pas voir / implémenter 'Task' mais il est implémenté quelque part dans une classe parent. De même, dans notre exemple, appeler un déjà construit MethodAsync()est beaucoup plus facile que d'implémenter cette méthode avec un Task( MethodTask()) nous-mêmes. La plupart des développeurs ont du mal à se repérer Taskslors de la conversion d'un code en code asynchrone.

Conseil: essayez de trouver une implémentation Async existante (comme MethodAsyncou ToListAsync) pour sous-traiter la difficulté. Nous avons donc seulement besoin de traiter avec Async et d'attendre (ce qui est facile et assez similaire au code normal)

Problème: changez rapidement une implémentation réelle du code normal en fonctionnement asynchrone: 2 minutes

La ligne de code indiquée ci-dessous dans Data Layer a commencé à se rompre (à de nombreux endroits). Parce que nous avons mis à jour une partie de notre code du framework .Net 4.2. * Vers le core .Net. Nous avons dû résoudre ce problème en 1 heure partout dans l'application!

var myContract = query.Where(c => c.ContractID == _contractID).First();

peasy facile!

  1. Nous avons installé le package de nuget EntityFramework car il contient QueryableExtensions. Ou en d'autres termes, il fait l'implémentation Async (tâche), afin que nous puissions survivre avec du code simple Asyncet await.
  2. namespace = Microsoft.EntityFrameworkCore

la ligne de code d'appel a changé comme ça

var myContract = await query.Where(c => c.ContractID == _contractID).FirstAsync();
  1. La signature de la méthode est passée de

    Contract GetContract(int contractnumber)

    à

    async Task<Contract> GetContractAsync(int contractnumber)

  2. la méthode d'appel a également été affectée: a GetContractAsync(123456);été appelée en tant queGetContractAsync(123456).Result;

  3. Nous l'avons changé partout en 30 minutes!

Mais l'architecte nous a dit de ne pas utiliser la bibliothèque EntityFramework juste pour ça! Oops! drame! Ensuite, nous avons fait une implémentation de tâche personnalisée (yuk). Que vous savez comment. Toujours facile! ..encore yuk ..

Où ensuite? Il y a une merveilleuse vidéo rapide que nous pourrions regarder sur la conversion des appels synchrones en asynchrones dans ASP.Net Core , c'est peut-être probablement la direction que l'on prendrait après avoir lu ceci.

Nuages ​​bleus
la source
réponse fantastique! cela m'a aidé une tonne
cklimowski
1
Bonne réponse. Vous voudrez peut-être corriger quelques petites choses comme: (a) la mention de ".Net framework 4.2" (aucune version telle que je sache, n'existe) (b) la casse dans EntityFrameWork => EntityFramework
immitev
15

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

// Starts counting to a large number 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;
}
Tone Škoda
la source
2
Je vous remercie! la première réponse qui fait un peu de travail au lieu d'attendre.
Jeffnl
merci de montrer task.Wait();et comment il peut être utilisé pour éviter l'async / attendre l'enfer: P
encoder
12

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'obtenir un débit bien meilleur, 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 au démarrage ou ont une charge en rafale (où la concurrence augmente soudainement), rendre ces appels de service Web asynchrones augmentera la réactivité de votre application. Une demande asynchrone prend le même temps à traiter qu'une demande synchrone. Par exemple, si une demande effectue un appel de service Web qui nécessite deux secondes pour se terminer, la demande prend deux secondes, qu'elle soit effectué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, les demandes asynchrones 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 appellent des opérations de longue durée.

Lex Li
la source
12

Async & Attendre une explication simple

Analogie simple

Une personne peut attendre son train du matin. C'est tout ce qu'ils font car c'est leur tâche principale qu'ils effectuent actuellement. (programmation synchrone (ce que vous faites normalement!))

Une autre personne peut attendre son train du matin pendant qu'elle fume une cigarette, puis boire son café. (Programmation asynchrone)

Qu'est-ce que la programmation asynchrone?

La programmation asynchrone est l'endroit où un programmeur choisira d'exécuter une partie de son code sur un thread distinct du thread principal d'exécution, puis notifiera le thread principal à la fin.

Que fait réellement le mot-clé async?

Préfixe du mot-clé async à un nom de méthode comme

async void DoSomething(){ . . .

permet au programmeur d'utiliser le mot-clé wait lors de l'appel de tâches asynchrones. C'est tout.

Pourquoi est-ce important?

Dans de nombreux systèmes logiciels, le thread principal est réservé aux opérations spécifiquement liées à l'interface utilisateur. Si j'exécute un algorithme récursif très complexe qui prend 5 secondes pour se terminer sur mon ordinateur, mais je l'exécute sur le thread principal (thread d'interface utilisateur) Lorsque l'utilisateur essaie de cliquer sur quoi que ce soit sur mon application, il semblera être gelé car mon thread principal a été mis en file d'attente et traite actuellement beaucoup trop d'opérations. Par conséquent, le thread principal ne peut pas traiter le clic de souris pour exécuter la méthode à partir du clic de bouton.

Quand utilisez-vous Async et Await?

Utilisez les mots clés asynchrones idéalement lorsque vous faites quoi que ce soit qui n'implique pas l'interface utilisateur.

Disons donc que vous écrivez un programme qui permet à l'utilisateur de dessiner sur son téléphone mobile, mais toutes les 5 secondes, il va vérifier la météo sur Internet.

Nous devrions attendre l'appel, le sondage appelle toutes les 5 secondes au réseau pour obtenir la météo car l'utilisateur de l'application doit continuer à interagir avec l'écran tactile mobile pour dessiner de jolies images.

Comment utilisez-vous Async et Await

Dans le prolongement de l'exemple ci-dessus, voici un pseudo-code pour l'écrire:

    //ASYNCHRONOUS
    //this is called using the await keyword every 5 seconds from a polling timer or something.

    async Task CheckWeather()
    {
        var weather = await GetWeather();
        //do something with the weather now you have it
    }

    async Task<WeatherResult> GetWeather()
    {

        var weatherJson = await CallToNetworkAddressToGetWeather();
        return deserializeJson<weatherJson>(weatherJson);
    }

    //SYNCHRONOUS
    //This method is called whenever the screen is pressed
    void ScreenPressed()
    {
        DrawSketchOnScreen();
    }

Notes supplémentaires - mise à jour

J'ai oublié de mentionner dans mes notes d'origine qu'en C #, vous ne pouvez attendre que les méthodes qui sont enveloppées dans des tâches. par exemple, vous pouvez attendre cette méthode:

// awaiting this will return a string.
// calling this without await (synchronously) will result in a Task<string> object.
async Task<string> FetchHelloWorld() {..

Vous ne pouvez pas attendre des méthodes qui ne sont pas des tâches comme celle-ci:

async string FetchHelloWorld() {..

N'hésitez pas à consulter le code source de la classe Task ici .

James Mallon
la source
4
Merci d'avoir pris le temps d'écrire celui-ci.
Prashant
10

Async / Await

En fait, Async / Await sont une paire de mots-clés qui ne sont que du sucre syntaxique pour créer 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]
        });
    }

Le code ci-dessus présente plusieurs inconvénients. Les erreurs ne sont pas transmises et c'est difficile à lire. Mais Async et Await viennent 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 dans les méthodes Async. Cela présente certains avantages:

  • Renvoie le résultat de la tâche
  • crée automatiquement un rappel
  • vérifie les erreurs et les laisse bouillonner dans la pile d'appels (seulement jusqu'à des appels sans attente dans la pile d'appels)
  • 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

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

Voici une comparaison entre les solutions en attente et les solutions en attente

Voici 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]
        });
        // goes directly further
        // WITHOUT waiting until the task is finished

        // [RUNS ON MAIN THREAD]

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

Voici 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 toute exception ici est avalée en mode release:

    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 destinés au calcul parallèle. Ils sont utilisés pour ne pas bloquer votre thread principal. Lorsqu'il s'agit d'applications asp.net ou 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 pas ou même plantera.

Consultez ms docs pour plus d'exemples.

Hakim
la source
9

Pour être honnête, je pense toujours que la meilleure explication est celle du futur et des promesses sur Wikipedia: http://en.wikipedia.org/wiki/Futures_and_promises

L'idée de base est que vous disposez d'un pool séparé de threads qui exécutent les tâches de manière asynchrone. Lors de son utilisation. L'objet fait cependant 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 dans le pool de threads autrement.

De là, vous pouvez optimiser les choses: certaines opérations peuvent être implémentées en mode asynchrone et vous pouvez optimiser des choses comme les E / S de fichiers et la communication réseau en regroupant les demandes suivantes et / ou en les réorganisant. Je ne suis pas sûr que ce soit déjà dans le cadre de tâches de Microsoft - mais si ce n'est pas le cas, ce serait l'une des premières choses que j'ajouterais.

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

atlaste
la source
8

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 les 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: entrez la description de l'image ici

vibs2006
la source
3
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;
    }
}
Weslley Rufino de Lima
la source
3

À un niveau supérieur:

1) Le mot clé Async active l'attente et c'est tout. Le mot clé asynchrone n'exécute pas la méthode dans un thread séparé. La première méthode f async s'exécute de manière synchrone jusqu'à ce qu'elle atteigne l'attente d'une tâche longue.

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

3) Au moment où les rencontres du thread principal attendent sur une tâche longue ou lorsque le travail réel est démarré, le thread principal revient à l'appelant de la méthode actuelle.

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

5) Attendre la tâche de traitement, s'exécutera désormais sur un thread distinct du pool de threads.

6) Lorsque cette tâche d'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();
        }

    }
}
ABajpai
la source
2

La façon dont je comprends aussi, il devrait y avoir un troisième mandat ajouté au mélange: Task.

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

Taskest le retour de la asyncfonction. Il s'exécute de manière asynchrone.

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

Si, à la place, vous affectez le retour d'une asyncfonction (c'est Task-à- dire ) à une variable, lorsque l'exécution de code atteint cette ligne, elle continue juste au- delà de cette ligne dans la fonction environnante pendant que l' Taskexécution s'exécute de manière asynchrone.

user21306
la source
1

les utilise-t-il pour générer des threads d'arrière-plan pour effectuer une logique de longue durée?

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

Les mots clés asynchrones et en attente n'entraînent pas la création de threads supplémentaires. Les méthodes asynchrones ne nécessitent pas de multithreading car une méthode asynchrone ne s'exécute pas sur son propre thread. La méthode s'exécute sur le contexte de synchronisation actuel et utilise l'heure sur le thread uniquement lorsque la méthode est active.

Dmitry G.
la source
1

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 lorsque la tâche est terminée. L'opérateur d'attente est appliqué à getContentsTask pour suspendre l'exécution dans SumPageSizesAsync jusqu'à ce que getContentsTask soit terminé. En attendant, le contrôle est retourné à l'appelant de SumPageSizesAsync. Lorsque getContentsTask est terminé, l'expression d'attente s'évalue 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);
    // . . .
}
lazydeveloper
la source
1

Ci-dessous, le code qui lit le fichier Excel en ouvrant la boîte de dialogue, puis utilise async et attend pour exécuter en mode asynchrone le code qui lit une par une la ligne 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
    {
    }
}
Zaheer Abbas
la source
0

Les réponses ici sont utiles comme guide général sur wait / async. Ils contiennent également des détails sur la façon dont wait / async est câblé. Je voudrais partager avec vous une expérience pratique que vous devez savoir avant d'utiliser ce modèle de conception.

Le terme «attendre» est littéral, donc quel que soit le thread sur lequel vous l'appelez attendra le résultat de la méthode avant de continuer. Au premier plan , c'est un désastre . Le thread de premier plan supporte la charge de la construction de votre application, y compris les vues, les modèles de vue, les animations initiales et tout ce que vous avez démarré avec ces éléments. Donc, quand vous attendez le fil de premier plan, vous vous arrêtez l'application. L'utilisateur attend et attend quand rien ne semble se produire. Cela offre 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 sur 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.

Stephen Marcus
la source
1
D'après ce que je comprends, async / awaitc'est du sucre syntaxique pour les rappels, cela n'a rien à voir avec le filetage. msdn.microsoft.com/en-us/magazine/hh456401.aspx Il s'agit d'un code non lié au processeur, par exemple en attente d'entrée ou d'un retard. Task.Runne devrait être utilisé que pour le code lié au CPU blog.stephencleary.com/2013/10/…
geometrikal
The term "await" is literal, so whatever thread you call it on will wait for the result of the method before continuing.Ce n'est pas vrai - peut-être que vous vouliez dire Task.Wait ()? Lorsque vous utilisez await, il définit le reste de la méthode comme une continuation à exécuter lorsque tout ce que vous attendez est terminé. Il quitte la méthode dans laquelle vous l'avez utilisé, afin que l'appelant puisse continuer. Ensuite, lorsque la ligne en attente est réellement terminée, elle termine le reste de cette méthode sur un thread (généralement un thread de travail).
Don Cheadle
@geometrikal, au cœur même, async/awaitconsiste à libérer des threads .NET. Lorsque vous effectuez awaitune opération véritablement asynchrone (telle que File.WriteAsync de .NET), elle suspend le reste de la méthode que vous avez utilisée await, afin que l'appelant puisse continuer et potentiellement terminer son objectif. Aucun thread ne bloque ou n'attend l' awaitopération -ed. Une fois l'opération awaitterminée, le reste de la async/awaitméthode est placé sur un thread et exécuté (semblable à une idée de rappel).
Don Cheadle