it-swarm.dev

Come e quando usare 'async' e 'attendere'

Dalla mia comprensione di una delle cose principali che async E await do è di rendere il codice facile da scrivere e leggere - ma li sta usando uguale a generare thread in background per eseguire una logica di lunga durata?

Attualmente sto provando l'esempio più semplice. Ho aggiunto alcuni commenti in linea. Puoi chiarirlo per me?

// 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

Quando si utilizza async e await il compilatore genera una macchina a stati in background.

Ecco un esempio su cui spero di poter spiegare alcuni dei dettagli di alto livello che stanno succedendo: 

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, quindi cosa succede qui:

  1. Task<int> longRunningTask = LongRunningOperationAsync(); inizia a eseguire LongRunningOperation

  2. Il lavoro indipendente viene svolto assumendo il thread principale (ID thread = 1), quindi viene raggiunto await longRunningTask

    Ora, se longRunningTask non ha finito ed è ancora in esecuzione, MyMethodAsync() tornerà al suo metodo di chiamata, quindi il thread principale non verrà bloccato. Una volta eseguita longRunningTask, un thread dal ThreadPool (può essere un qualsiasi thread) restituirà MyMethodAsync() nel suo contesto precedente e continuerà l'esecuzione (in questo caso stampando il risultato sulla console). 

Un secondo caso sarebbe che longRunningTask ha già terminato la sua esecuzione e il risultato è disponibile. Quando raggiungiamo il await longRunningTask abbiamo già il risultato, quindi il codice continuerà ad essere eseguito sullo stesso thread. (in questo caso stampa i risultati su console). Ovviamente questo non è il caso dell'esempio sopra, in cui è coinvolto un Task.Delay(1000)

629
Dan Dinu

Oltre alle altre risposte, dai un'occhiata a await (C # Reference)

e più specificamente nell'esempio incluso, spiega la tua situazione un po '

Il seguente esempio di Windows Form illustra l'uso dell'attesa in un file metodo asincrono, WaitAsynchronouslyAsync. Contrasto il comportamento di questo metodo con il comportamento di WaitSynchronously. Senza aspettare operatore applicato a un'attività, WaitSynchronously viene eseguito in modo sincrono nonostante l'uso del modificatore asincrono nella sua definizione e una chiamata a Thread.Sleep nel suo corpo.

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

Dalla mia comprensione, una delle cose principali che asincrona e attende è rendere il codice facile da scrivere e leggere.

Devono rendere il codice asincrono facile da scrivere e leggere, sì.

È la stessa cosa che generare thread in background per eseguire una logica di lunga durata?

Affatto.

// Non riesco a capire perché questo metodo deve essere contrassegnato come "asincrono".

La parola chiave async abilita la parola chiave await. Quindi qualsiasi metodo che usa await deve essere marcato async.

// questa linea viene raggiunta dopo 5 secondi di sospensione dal metodo DoSomethingAsync (). Non dovrebbe essere raggiunto immediatamente? 

No, perché i metodi async non vengono eseguiti su un altro thread per impostazione predefinita.

// è eseguito su un thread in background?

No.


Puoi trovare il mio async/await intro utile. Anche i documenti MSDN ufficiali sono insolitamente buoni (in particolare la sezione TAP , e il team async ha pubblicato un eccellente FAQ .

122
Stephen Cleary

Spiegazione

Ecco un rapido esempio di async/attendere ad alto livello. Ci sono molti più dettagli da considerare oltre a questo.

Nota: Task.Delay(1000) simula l'esecuzione del lavoro per 1 secondo. Penso che sia meglio pensarlo come in attesa di una risposta da una risorsa esterna. Poiché il nostro codice è in attesa di una risposta, il sistema può impostare il task in esecuzione su un lato e tornare ad esso una volta che è finito. Nel frattempo, può fare qualche altro lavoro su quel thread.

Nell'esempio seguente, il primo blocco sta facendo esattamente questo. Avvia immediatamente tutte le attività (le righe Task.Delay) e le mette da parte. Il codice si interrompe sulla riga await a fino a quando viene eseguito il ritardo di 1 secondo prima di passare alla riga successiva. Poiché b, c, d e e hanno iniziato a essere eseguiti quasi alla stessa ora di a (a causa della mancanza di attesa), in questo caso dovrebbero terminare all'incirca nello stesso momento.

Nell'esempio seguente, il secondo blocco sta avviando un'attività e sta aspettando che finisca (è ciò che fa await) prima di iniziare le attività successive. Ogni iterazione di questo richiede 1 secondo. await sta mettendo in pausa il programma e sta aspettando il risultato prima di continuare. Questa è la principale differenza tra il primo e il secondo blocco.

Esempio

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);

PRODUZIONE:

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)

Ulteriori informazioni su SynchronizationContext

Nota: è qui che le cose diventano un po 'annebbiate per me, quindi se mi sbaglio su qualcosa, correggimi e aggiornerò la risposta. È importante avere una comprensione di base di come funziona, ma puoi cavartela senza essere un esperto finché non usi mai ConfigureAwait(false), anche se probabilmente perderai qualche opportunità di ottimizzazione, presumo.

C'è un aspetto di ciò che rende il concetto asincrono/atteso in qualche modo più complicato da comprendere. Questo è il fatto che in questo esempio, tutto sta accadendo sullo stesso thread (o almeno quello che sembra essere lo stesso thread in relazione al suo SynchronizationContext). Per impostazione predefinita, await ripristinerà il contesto di sincronizzazione del thread originale su cui è stato eseguito. Ad esempio, in ASP.NET hai un HttpContext che è legato ad un thread quando arriva una richiesta. Questo contesto contiene cose specifiche della richiesta Http originale come l'oggetto Request originale che ha cose come lingua, indirizzo IP, intestazioni, ecc. Se passi i thread a metà dell'elaborazione di qualcosa, potresti potenzialmente cercare di estrarre informazioni da questo oggetto su un HttpContext diverso che potrebbe essere disastroso. Se sai che non utilizzerai il contesto per nulla, puoi scegliere di "non curarsene". Questo in pratica consente al tuo codice di girare su un thread separato senza portare con sé il contesto.

Come si ottiene questo? Per impostazione predefinita, il codice await a; sta effettivamente supponendo che tu voglia catturare e ripristinare il contesto:

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

Se si desidera consentire al codice principale di continuare su un nuovo thread senza il contesto originale, è sufficiente utilizzare false anziché true in modo che sappia che non è necessario ripristinare il contesto.

await a.ConfigureAwait(false);

Dopo che il programma è stato messo in pausa, continuerà potenzialmente su un thread completamente diverso con un contesto diverso. Da qui il miglioramento delle prestazioni: potrebbe continuare su qualsiasi thread disponibile senza dover ripristinare il contesto originale con cui è iniziato.

Questa roba è confusa? Hell yeah! Riesci a capirlo? Probabilmente! Una volta che hai compreso i concetti, passa alle spiegazioni di Stephen Cleary che tendono ad essere più orientate verso qualcuno con una comprensione tecnica di asincrono/attesa già.

116
Joe Phillips

Mostrando le spiegazioni di cui sopra in azione in un semplice programma di console -

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;
    }
}

E l'output è:

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

Così,

  1. Main avvia il metodo long running tramite TestAsyncAwaitMethods. Questo ritorna immediatamente senza interrompere il thread corrente e immediatamente vediamo il messaggio 'Premi un tasto qualsiasi per uscire'
  2. Tutto questo mentre, LongRunningMethod è in esecuzione in background. Una volta completato, un altro thread di Threadpool riprende questo contesto e visualizza il messaggio finale

Quindi, il thread non è bloccato. 

47
sppc42

Penso che tu abbia scelto un cattivo esempio con System.Threading.Thread.Sleep

Punto di un async Il compito è di lasciarlo eseguire in background senza bloccare il thread principale, come fare un DownloadFileAsync

System.Threading.Thread.Sleep non è qualcosa che si sta "facendo", ma semplicemente dorme, e quindi la tua prossima linea viene raggiunta dopo 5 secondi ...

Leggi questo articolo, penso che sia una grande spiegazione del concetto async e await: http://msdn.Microsoft.com/en-us/library/vstudio/hh191443.aspx

38
Vnuk

Ecco un programma di console rapido per rendere chiaro a coloro che seguono. Il metodo "TaskToDo" è il metodo a lunga esecuzione che si desidera rendere asincrona. Rendendolo eseguito Async viene eseguito dal metodo TestAsync. Il metodo dei cicli di test esegue semplicemente le attività "TaskToDo" e le esegue in modo asincrono. Lo si può vedere nei risultati perché non vengono completati nello stesso ordine da esecuzione a esecuzione, poiché vengono segnalati al thread dell'interfaccia utente della console al completamento. Semplicistico, ma penso che gli esempi semplicistici tirino fuori il nucleo del modello meglio degli esempi più coinvolti:

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

Questa risposta mira a fornire alcune informazioni specifiche per ASP.NET.

Utilizzando async/await nel controller MVC, è possibile aumentare l'utilizzo del pool di thread e ottenere un throughput molto migliore, come spiegato nell'articolo seguente,

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

Nelle applicazioni Web che visualizza un numero elevato di richieste simultanee in start-up o ha un carico esplosivo (dove la concorrenza aumenta improvvisamente), rendendo tali chiamate di servizi Web asincrone aumenterà il reattività della vostra applicazione. Una richiesta asincrona accetta il la stessa quantità di tempo da elaborare come richiesta sincrona. Per esempio, se una richiesta effettua una chiamata al servizio web che richiede due secondi per completo, la richiesta impiega due secondi se viene eseguita in modo sincrono o asincrono. Tuttavia, durante una chiamata asincrona, un thread non è bloccato dal rispondere ad altre richieste mentre esso attende la prima richiesta da completare. Pertanto, asincrono le richieste impediscono l'accodamento delle richieste e la crescita del pool di thread quando ci sono molte richieste simultanee che invocano operazioni a lungo termine.

11
Lex Li

Tutte le risposte qui utilizzano Task.Delay () o qualche altra funzione asincrona incorporata. Ma ecco il mio esempio che non usa nessuna di quelle funzioni asincrone:

    // 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

Ad essere onesti, continuo a pensare che la migliore spiegazione sia quella sul futuro e le promesse su Wikipedia: http://en.wikipedia.org/wiki/Futures_and_promises

L'idea di base è che si dispone di un pool separato di thread che eseguono attività in modo asincrono. Quando lo si usa. L'oggetto tuttavia promette che eseguirà l'operazione in un dato momento e ti darà il risultato quando lo richiedi. Ciò significa che verrà bloccato quando si richiede il risultato e non è stato completato, ma verrà eseguito nel pool di thread in caso contrario.

Da lì puoi ottimizzare le cose: alcune operazioni possono essere implementate async e puoi ottimizzare cose come il file IO e le comunicazioni di rete raggruppando insieme le richieste successive e/o riordinandole. Non sono sicuro che questo sia già nel framework delle attività di Microsoft, ma se non lo fosse sarebbe una delle prime cose che aggiungerei.

È possibile implementare in futuro il modello di ordinamento con rese in C # 4.0. Se vuoi sapere come funziona esattamente, posso consigliare questo link che fa un lavoro decente: http://code.google.com/p/fracture/source/browse/trunk/Squared/TaskLib/ . Tuttavia, se inizi a giocarci da solo, noterai che hai davvero bisogno del supporto linguistico se vuoi fare tutte le cose interessanti - che è esattamente ciò che Microsoft ha fatto.

9
atlaste

Vedere questo fiddle https://dotnetfiddle.net/VhZdLU (e migliorarlo se possibile) per l'esecuzione di semplice applicazione console che mostra gli usi di Task, Task.WaitAll (), async e attende gli operatori nello stesso programma. 

Questo violino dovrebbe chiarire il tuo concetto di ciclo di esecuzione. 

Ecco il codice di esempio

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;
    }   

}

Traccia proveniente dalla finestra di output: enter image description here

6
vibs2006

Per l'apprendimento più veloce ..

  • Comprendere il flusso di esecuzione del metodo (con un diagramma): 3 minuti

  • Domanda introspezione (apprendimento sake): 1 min

  • Passa rapidamente lo zucchero della sintassi: 5 minuti

  • Condividi la confusione di uno sviluppatore: 5 minuti

  • Problema: modifica rapidamente un'implementazione del codice normale da codice reale a Codice asincrono: 2 minuti

  • Dove andare?

Comprendi il flusso di esecuzione del metodo (con un diagramma): 3 minuti

In questa immagine, concentrati solo su # 6  enter image description here

Al passaggio 6: AccessTheWebAsync () ha esaurito il lavoro che può fare senza un risultato di getStringTask. Pertanto, AccessTheWebAsync utilizza un operatore di attesa per sospenderne l'avanzamento e restituire il controllo (rendimento) al chiamante. AccessTheWebAsync restituisce un'attività (di valore restituito stringa) al chiamante. L'attività rappresenta una promessa per produrre un risultato stringa. Ma quando restituirà la chiamata? Una seconda chiamata di nuovo?

Il chiamante di AccessTheWebAsync () non ha fatto altro che aspettare (avrebbe potuto eseguire alcune attività interne e quindi attendere se necessario). Quindi il chiamante attende AccessTheWebAsync e AccessTheWebAsync è in attesa di GetStringAsync al momento.

Ricorda, il metodo è già stato restituito, non può tornare di nuovo (non è la seconda volta). Quindi, come lo saprà il chiamante? Si tratta di Compiti! L'attività è stata restituita. L'attività è stata attesa per (non metodo, non valore). Il valore sarà impostato nell'attività. Lo stato dell'attività sarà impostato per il completamento. Il chiamante controlla solo l'attività. Ulteriori letture per dopo qui .

Domanda introspezione per l'apprendimento: 1 min

Cerchiamo di adattare la domanda un po ': 

Come e quando usare  async e await  Tasks?

Perché imparare Task copre automaticamente l'altro 2. Per imparare almeno. Naturalmente, questa è una risposta alla tua domanda su async e await.

Passa rapidamente allo zucchero della sintassi: 5 minuti

  • Prima della conversione (metodo originale)

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

  • Un altro metodo Task-call per chiamare il metodo sopra

    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; }

Abbiamo parlato dell'attesa o asincrona? No. Chiama il metodo sopra e ottieni un compito. Che puoi monitorare. Sai già cosa restituisce l'attività .. un numero intero.

  • Chiamare un'attività è un po 'complicato. Chiamiamo MethodTask ()

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

Stiamo aspettando che il compito sia finito. Da qui il await. Poiché usiamo attendere, dobbiamo usare async (obbligatorio) e MethodAsync con 'Async' come prefisso (standard di codifica). Ulteriori letture per dopo qui

Condividi la confusione di uno sviluppatore: 5 minuti

Uno sviluppatore ha commesso l'errore di non implementare Task ma funziona ancora! Cerca di capire la domanda e solo la risposta accettata fornita qui . Spero tu abbia letto e capito pienamente. Allo stesso modo, nel nostro esempio, chiamare un MethodAsync() già costruito è molto più semplice dell'implementazione di quel metodo con un Task (MethodTask()) selfself. La maggior parte degli sviluppatori ha difficoltà a comprendere Tasks durante la conversione di un codice in uno asincrono.

Suggerimento: cerca di trovare un'implementazione Async esistente (come MethodAsync o ToListAsync) per esternalizzare la difficoltà. Quindi abbiamo solo bisogno di gestire Async e attendere (che è facile e abbastanza simile al normale codice)

Problema: modifica rapidamente un'implementazione del codice normale nel mondo reale Operazione asincrona: 2 minuti

La riga di codice mostrata di seguito in Livello dati ha iniziato a interrompersi (molti posti). Perché abbiamo aggiornato parte del nostro codice da .Net framework 4.2 a .Net core. Abbiamo dovuto risolvere questo problema in 1 ora per tutta l'applicazione! 

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

vai tranquillo!

  1. EntityFrameWork nuget (ha QueryableExtensions)
  2. namespace = Microsoft.EntityFrameworkCore 

il codice è cambiato in questo modo

var myContract = await query.Where(c => c.ContractID == _contractID).FirstAsync();
  1. Firma del metodo modificata da

    Contract GetContract(int contractnumber) 

    async Task<Contract> GetContractAsync(int contractnumber)

  2. anche il metodo di chiamata è stato influenzato: GetContractAsync(123456); è stato chiamato come GetContractAsync(123456).Result;

  3. Lo abbiamo cambiato ovunque in 30 minuti!

Ma l'architetto ci ha detto di non usare la libreria EntityFrameWork solo per questo! oops! Dramma! Quindi abbiamo realizzato un'implementazione personalizzata delle attività. Quale sai come. Ancora facile! 

Dove andare? C'è un meraviglioso video veloce che potremmo vedere su Conversione di chiamate sincrone in Asincrono in ASP.Net Core , perché è probabile che la direzione che si dovrebbe seguire dopo aver letto questo. 

4
Blue Clouds
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;
    }
}

A un livello superiore:

1) La parola chiave Async abilita l'attesa e questo è tutto ciò che fa. La parola chiave Async non esegue il metodo in un thread separato. Il metodo f async di inizio viene eseguito in modo sincrono finché non viene eseguito l'atteso su un'attività che richiede molto tempo.

2) È possibile attendere su un metodo che restituisce Attività o Attività di tipo T. Non è possibile attendere il metodo di annullamento asincrono.

3) Nel momento in cui gli incontri di thread principale sono in attesa di un'attività che richiede tempo o quando viene avviato il lavoro effettivo, il thread principale ritorna al chiamante del metodo corrente. 

4) Se il thread principale vede attendere un'attività che è ancora in esecuzione, non lo attende e ritorna al chiamante del metodo corrente. In questo modo, l'applicazione rimane reattiva.

5) In attesa sull'attività di elaborazione, ora verrà eseguito su un thread separato dal pool di thread.

6) Quando questo task di attesa è completato, tutto il codice sottostante verrà eseguito dal thread separato

Di seguito è riportato il codice di esempio. Eseguilo e controlla l'id del 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

Per come la vedo io, dovrebbe esserci un terzo termine aggiunto al mix: Task.

Async è solo un qualificatore che hai messo sul tuo metodo per dire che è un metodo asincrono.

Task è il ritorno della funzione async. Esegue in modo asincrono.

È await un compito. Quando l'esecuzione del codice raggiunge questa linea, il controllo torna indietro al chiamante della funzione originale circostante.

Se invece, si assegna il ritorno di una funzione async (cioè Task) a una variabile, quando l'esecuzione del codice raggiunge questa linea, solo continua oltre quella riga nella funzione circostante while Task esegue in modo asincrono.

2
user21306

Nel seguente codice, il metodo HttpClient GetByteArrayAsync restituisce un'attività, getContentsTask. L'attività è una promessa per produrre l'array di byte effettivo quando l'attività è completa. L'operatore attende viene applicato a getContentsTask per sospendere l'esecuzione in SumPageSizesAsync fino a quando getContentsTask non è completo. Nel frattempo, il controllo viene restituito al chiamante di SumPageSizesAsync. Quando getContentsTask è terminato, l'espressione await restituisce una matrice di byte.

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/Await

In realtà Async/Attendi sono una coppia di parole chiave che sono solo zucchero sintattico per la creazione di una richiamata di un'attività asincrona.

Prendi ad esempio questa operazione:

    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]
        });
    }

Questo metodo ha diversi svantaggi. Gli errori non vengono trasmessi ed è estremamente difficile da leggere . Ma Async e Await arrivano per aiutarci:

    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");

    }

Aspettare le chiamate devono essere in metodi Async. Questo ha alcuni vantaggi:

  • Restituisce il risultato dell'attività
  • crea automaticamente un callback
  • controlla la presenza di errori e li fa esplodere nel callstack (solo fino a zero chiamate in attesa nel callstack)
  • aspetta il risultato
  • libera il thread principale
  • esegue la richiamata sul thread principale
  • utilizza un thread di lavoro dal threadpool per l'attività
  • rende il codice facile da leggere
  • e molto di più

NOTE: Async e Await vengono utilizzate con chiamate asincrone not per renderle. Devi usare Task Libary per questo, come Task.Run ().

Ecco un confronto tra l'attesa e nessuno attende soluzioni

Questa è la soluzione async none:

    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;
    }

Questo è il metodo asincrono:

    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;
    }

In realtà puoi chiamare un metodo asincrono senza la parola chiave await, ma ciò significa che qualsiasi eccezione qui viene ingoiata in modalità di rilascio:

    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 e Await non sono pensati per il calcolo parallelo. Sono usati per non bloccare il thread principale. Se si tratta di asp.net o di applicazioni Windows. Il blocco del thread principale a causa di una chiamata di rete è una cosa negativa. Se lo fai, la tua app non risponderà o potrebbe bloccarsi.

Controlla ms doc per ottenere alcuni esempi.

1
Hakim

li sta usando uguale a generare thread in background per eseguire lunghe logica di durata?

Questo articolo MDSN: programmazione asincrona con async e await (C #) lo spiega esplicitamente:

Le parole chiave asincrone e attese non provocano thread aggiuntivi creato. I metodi asincroni non richiedono il multithreading perché async il metodo non funziona sul proprio thread. Il metodo viene eseguito sul corrente contesto di sincronizzazione e utilizza il tempo sul thread solo quando il metodo è attivo.

1
Dmitry G.

Di seguito sono indicate le regole del pollice che uso per decidere quando utilizzare le promesse e quando utilizzare async

  1. La funzione asincrona restituisce una promessa. Il contrario è anche vero. Ogni funzione che restituisce una promessa può essere considerata come la funzione asincrona attende per chiamare una funzione asincrona e attendere che si risolva o rifiuti.

  2. attendono blocchi l'esecuzione del codice all'interno della funzione asincrona in cui si trova. Se l'output di function2 dipende dall'output di function1, allora utilizzo Wait.

  3. Se due funzioni possono essere eseguite in parallelo, creare due diverse funzioni asincrone e quindi eseguirle in parallelo.

  4. Per eseguire promesse in parallelo, creare una serie di promesse e quindi utilizzare Promise.all (promisesArray) Ogni volta che si utilizza attendere, ricordare che si sta scrivendo il codice di blocco. Nel tempo tendiamo a trascurarlo.

  5. Invece di creare enormi funzioni asincrone con molte attese asyncFunction (), è meglio creare funzioni asincrone più piccole. In questo modo saremo consapevoli di non scrivere troppo del codice di blocco. Un altro vantaggio dell'utilizzo di piccole funzioni asincrone è che ti costringi a pensare quali sono le funzioni asincrone che possono essere eseguite in parallelo.

  6. Se il tuo codice contiene codice di blocco è meglio renderlo una funzione asincrona. In questo modo ti stai assicurando che qualcun altro possa utilizzare la tua funzione in modo asincrono.

  7. Attivando le funzioni asincrone dal codice di blocco, si attiva l'utente che chiamerà la propria funzione per decidere sul livello di asintonicità che desidera.

Spero che questo ti aiuti a decidere facilmente quando usare le promesse e quando usare le promesse e quando aspettare

0
Sunny Sultan

Di seguito è riportato il codice che legge il file Excel aprendo la finestra di dialogo e quindi utilizza async e attende di eseguire il codice asincrono che legge una riga per volta da Excel e si collega alla griglia 

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

Async & Await Simple Explanation

Simple Analogy

Una persona può aspetta per il treno del mattino. Questo è tutto ciò che stanno facendo in quanto questo è il loro compito principale che stanno attualmente eseguendo. (programmazione sincrona (cosa fai normalmente!))

Un'altra persona può attende il loro treno del mattino mentre fuma una sigaretta e poi beve il loro caffè. (Programmazione asincrona)

Cos'è la programmazione asincrona?

La programmazione asincrona è quella in cui un programmatore sceglierà di eseguire parte del suo codice su un thread separato dal thread principale di esecuzione e quindi notificherà il thread principale al suo completamento.

Cosa fa effettivamente la parola chiave async?

Prefixing come la parola chiave async a un nome di metodo 

async void DoSomething(){ . . .

consente al programmatore di utilizzare la parola chiave await quando chiama attività asincrone. Questo è tutto ciò che fa.

Perché questo è importante?

In molti sistemi software il thread principale è riservato per operazioni che riguardano specificamente l'interfaccia utente. Se eseguo un algoritmo ricorsivo molto complesso che richiede 5 secondi per essere completato sul mio computer, ma lo sto eseguendo sul thread principale (thread dell'interfaccia utente) Quando l'utente tenta di fare clic su qualsiasi cosa sulla mia applicazione, sembrerà che sia congelato come il mio thread principale ha messo in coda e attualmente sta elaborando troppe operazioni. Di conseguenza, il thread principale non può elaborare il clic del mouse per eseguire il metodo dal clic del pulsante.

Quando usi Async e Attendi?

Utilizza idealmente le parole chiave asincrone quando fai qualcosa che non coinvolge l'interfaccia utente. 

Quindi diciamo che stai scrivendo un programma che consente all'utente di disegnare sul proprio telefono cellulare, ma ogni 5 secondi controllerà il tempo su Internet.

Dovremmo essere in attesa della chiamata che il polling chiama ogni 5 secondi alla rete per ottenere il tempo in cui l'utente dell'applicazione deve continuare a interagire con il touch screen mobile per disegnare belle immagini.

Come usi Async e Await

Seguendo l'esempio sopra, ecco qualche pseudo codice su come scriverlo:

     //ASYNCHRONOUS
    //this is called every 5 seconds
    async void 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();
    }
0
James Mallon

Le risposte qui sono utili come guida generale sull'attesa/asincrono. Contengono anche alcuni dettagli su come è atteso/async cablato. Mi piacerebbe condividere con te alcune esperienze pratiche che dovresti conoscere prima di utilizzare questo modello di design.

Il termine "attendere" è letterale, quindi qualunque thread lo chiami attenderà il risultato del metodo prima di continuare. Nel thread foreground, questo è un disastro. Il thread in primo piano comporta l'onere di costruire la tua app, comprese le viste, i modelli di visualizzazione, le animazioni iniziali e qualsiasi altra cosa che hai a che fare con questi elementi. Quindi, quando aspetti il ​​thread in primo piano, fai stop l'app. L'utente aspetta e aspetta quando non sembra che accada nulla. Ciò fornisce un'esperienza utente negativa.

Puoi certamente aspettare un thread in background usando una varietà di mezzi:

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
{}

Il codice completo per queste osservazioni è in https://github.com/marcusts/xamarin-forms-annoyances . Vedi la soluzione chiamata AwaitAsyncAntipattern.sln.

Il sito GitHub fornisce anche collegamenti a una discussione più dettagliata su questo argomento.

0
Stephen Marcus