Come e quando utilizzare "asincrono" e "attendere"


1066

Dal mio punto di vista, una delle cose principali da fare asynce daawait fare è rendere il codice facile da scrivere e leggere - ma è usarli equivale a generare thread in background per eseguire logiche 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;
}

48
Inoltre, nel tuo esempio nota che ricevi un avviso quando compili il codice sopra. Prestare attenzione all'avvertimento . Ti sta dicendo che questo codice non ha senso.
Eric Lippert,

Risposte:


759

Quando si utilizza asynce awaitil compilatore genera una macchina a stati in background.

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

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 l'esecuzione LongRunningOperation

  2. Si lavora in modo indipendente supponiamo che il thread principale (ID thread = 1) await longRunningTasksia raggiunto.

    Ora, se longRunningTasknon è terminato ed è ancora in esecuzione, MyMethodAsync()tornerà al suo metodo di chiamata, quindi il thread principale non viene bloccato. Al longRunningTasktermine, un thread dal ThreadPool (può essere qualsiasi thread) tornerà al MyMethodAsync()suo contesto precedente e continuerà l'esecuzione (in questo caso stampa il risultato sulla console).

Un secondo caso sarebbe che longRunningTaskha già terminato la sua esecuzione e il risultato è disponibile. Quando si raggiunge il await longRunningTaskabbiamo già il risultato, quindi il codice continuerà ad essere eseguito sullo stesso thread. (in questo caso stampa il risultato sulla console). Ovviamente questo non è il caso dell'esempio sopra, in cui è Task.Delay(1000)coinvolto.


65
Perché abbiamo un "aspetto" con "Task.Delay (1000);" nel metodo asincrono LongRunningOperation?
Benison Sam,

3
@codea Nei commenti di Eric Lippert all'articolo ha collegato un articolo introduttivo a questo argomento in cui confronta specificamente la strategia DoEvents con async-waitit
Camilo Martinez,

13
Il thread @BenisonSam è un po 'vecchio, ma avevo la stessa domanda e ho cercato una risposta. Il motivo per "wait" è che se omettiamo "wait", LongRunningOperationAsync () tornerà immediatamente. In effetti il ​​compilatore darà un avviso se rimuoviamo l'attesa. Il post sul blog di Stephen Cleary blog.stephencleary.com/2011/09/… offre un eccellente esempio delle discussioni sul design.
shelbypereira,

70
Se ogni metodo asincrono deve avere un'attesa al suo interno e un'attesa può essere fatta solo su un metodo asincrono, quando si ferma?
Bruno Santos,

108
Questa risposta è chiaramente sbagliata. Questi molti voti causeranno una comprensione errata a molti utenti. La documentazione di MS dice chiaramente che nessun altro thread viene utilizzato quando si utilizza asincrono, wait. msdn.microsoft.com/en-us/library/mt674882.aspx Per favore qualcuno corregga la risposta. Per questo motivo ho perso un giorno intero.
Krishna Deepak,

171

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

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

È la stessa cosa della generazione di thread in background per eseguire logiche di lunga durata?

Affatto.

// Non capisco perché questo metodo debba essere contrassegnato come 'asincrono'.

La asyncparola chiave abilita la awaitparola chiave. Pertanto, qualsiasi metodo utilizzato awaitdeve essere contrassegnato async.

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

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

// Viene eseguito su un thread in background?

No.


La mia async/ awaitintro potrebbe essere utile. Anche i documenti ufficiali MSDN sono insolitamente buoni (in particolare la sezione TAP ) e il asyncteam ha pubblicato un'ottima FAQ .


6
Quindi non è in esecuzione su un thread in background, ma non si blocca. Ciò è possibile grazie alle API asincrone che utilizzano i callback invece di destreggiarsi con i thread. Si avvia un'operazione (I / O, socket, ..) e si ritorna a fare le proprie cose. Al termine dell'operazione, il sistema operativo invocherà il callback. Questo è ciò che fa Node.js o il framework Python Twisted e hanno anche delle belle spiegazioni.
Roman Plášil,

3
"La parola chiave asincrona abilita la parola chiave waitit. Pertanto, qualsiasi metodo che utilizza waitit deve essere contrassegnato come asincrono.", - ma perché? questa risposta non aiuta a capire perché il metodo deve essere contrassegnato come asincrono. Il compilatore non può semplicemente dedurre che il metodo è asincrono cercando all'interno delle parole chiave wait?
Stanislav

9
@Stanislav: ho un post sul blog che affronta questa domanda.
Stephen Cleary,

3
Chiarimento suggerito: No, perché i asyncmetodi non sono eseguiti su un altro thread per impostazione predefinita. Nel tuo esempio, la Sleep()chiamata all'interno DoSomethingAsync()blocca il thread corrente che impedisce l'esecuzione all'interno button1_Click()fino al DoSomethingAsync()completamento. Nota che mentre Thread.Sleep()blocca il thread in esecuzione,Task.Delay() does not.
DavidRR

166

Spiegazione

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

Nota: Task.Delay(1000)simula il 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 l'attività in esecuzione sul lato e tornare ad essa una volta terminata. Nel frattempo, può fare qualche altro lavoro su quel thread.

Nell'esempio seguente, il primo blocco sta facendo esattamente questo. Inizia immediatamente tutte le attività (le Task.Delaylinee) e le mette di lato. Il codice verrà messo in pausa sulla await ariga fino a quando non viene eseguito il ritardo di 1 secondo prima di passare alla riga successiva. Dato che b, ce d, etutti hanno iniziato a eseguire quasi nello stesso momento a(a causa della mancanza dell'attesa), in questo caso dovrebbero terminare all'incirca nello stesso momento.

Nell'esempio seguente, il secondo blocco sta iniziando un'attività e sta aspettando che finisca (che è quello che awaitfa) prima di iniziare le attività successive. Ogni iterazione richiede 1 secondo. È in awaitpausa il programma e in attesa del risultato prima di continuare. Questa è la differenza principale 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)

Informazioni aggiuntive su SynchronizationContext

Nota: è qui che le cose diventano un po 'confuse per me, quindi se sbaglio qualcosa, per favore correggimi e aggiornerò la risposta. È importante avere una conoscenza di base di come funziona, ma è possibile cavarsela senza essere un esperto, purché non si utilizzi mai ConfigureAwait(false), anche se probabilmente si perderà qualche opportunità di ottimizzazione, suppongo.

C'è un aspetto di ciò che rende il concetto async/ awaitun po 'più difficile da comprendere. Questo è il fatto che in questo esempio, tutto ciò sta accadendo sullo stesso thread (o almeno quello che sembra essere lo stesso thread rispetto al suo SynchronizationContext). Per impostazione predefinita, awaitripristinerà il contesto di sincronizzazione del thread originale su cui era in esecuzione. Ad esempio, in ASP.NET hai un oggetto HttpContextche è legato a un thread quando arriva una richiesta. Questo contesto contiene elementi specifici della richiesta Http originale come l'oggetto Richiesta originale che ha elementi come lingua, indirizzo IP, intestazioni, ecc. Se cambi thread a metà dell'elaborazione di qualcosa, potresti potenzialmente cercare di estrarre informazioni da questo oggetto su un altroHttpContextche potrebbe essere disastroso. Se sai che non utilizzerai il contesto per nulla, puoi scegliere di "non preoccupartene". Questo in pratica consente al tuo codice di essere eseguito su un thread separato senza invertire il contesto.

Come ci riesci? Per impostazione predefinita, il await a;codice in realtà presuppone che si desidera acquisire e ripristinare il contesto:

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

Se vuoi consentire al codice principale di continuare su un nuovo thread senza il contesto originale, devi semplicemente usare false invece di 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. È qui che verrebbe il miglioramento delle prestazioni: potrebbe continuare su qualsiasi thread disponibile senza dover ripristinare il contesto originale con cui è iniziato.

Questa roba è confusa? Inferno sì! Riesci a capirlo? Probabilmente! Una volta che hai una comprensione dei concetti, passa alle spiegazioni di Stephen Cleary che tendono ad essere più orientate verso qualcuno con una conoscenza tecnica di async/ awaitgià.


Diciamo se tutte queste attività stanno restituendo un int e se sto usando il risultato della prima attività nella seconda attività (o qualche calcolo) Sarebbe sbagliato?
veerendra gupta,

3
@veerendragupta yes. Sceglieresti consapevolmente di non eseguirli in modo asincrono in quel caso (perché non sono asincroni). Ci sono anche alcune altre cose da capire riguardo al contesto di configurazione in cui non entrerò qui
Joe Phillips,

Quindi await MethodCall()è uno spreco assoluto? Potresti anche rilasciare il await/ async?
Vitani,

2
@Jocie Non proprio. Quando chiami await, penso che rilasci il thread nel pool invece di tenerlo. Questo lo rende disponibile per l'uso altrove in attesa del ritorno dell'attività
Joe Phillips,

2
@JoePhillips Penso che quello che hai appena detto sia l'essenza di async / wait. Il thread chiamante viene liberato e può essere utilizzato da altri processi sulla macchina. Quando la chiamata in attesa è completa, viene utilizzato un nuovo thread per riprendere ciò che il chiamante ha iniziato in origine. Il chiamante sta ancora aspettando, ma il vantaggio è che nel frattempo viene liberato un thread. Questo è il vantaggio di async / wait?
Bob Horn,

147

Oltre alle altre risposte, dai un'occhiata a wait (Riferimenti per C #)

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

Il seguente esempio di Windows Form illustra l'uso di wait in un metodo asincrono, WaitAsynchronouslyAsync. Contrasta il comportamento di quel metodo con il comportamento di WaitSynchronously. Senza che un operatore wait sia applicato a un'attività, WaitSynchronous 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";
}

3
Grazie per la risposta. Ma WaitAsynchronouslyAsync () viene eseguito su un thread separato?
Dan Dinu,

32
Lo credo, dalla sezione Un'espressione wait non blocca il thread su cui sta eseguendo. Al contrario, fa sì che il compilatore registri il resto del metodo asincrono come continuazione dell'attività attesa. Il controllo quindi ritorna al chiamante del metodo asincrono. Al termine dell'attività, viene richiamata la sua continuazione e l'esecuzione del metodo asincrono riprende da dove era stata interrotta.
Adriaan Stander,

13
Secondo questo articolo MSDN , "Le parole chiave asincrone e attendono non causano la creazione di thread aggiuntivi .... un metodo asincrono non viene eseguito sul proprio thread". La mia comprensione è che alle parole chiave in attesa il framework salta in avanti (di nuovo al chiamante) per consentire l'esecuzione di tutto il codice indipendente possibile in attesa del completamento di lunghe operazioni. Penso che ciò significhi che una volta eseguito tutto il codice indipendente, se l'operazione lunga non è tornata, si bloccherà. Sto solo imparando questo ora, però.
Vimes,

9
@astander Quello non è corretto. Esso non esegue su un thread diverso. Pianifica semplicemente la continuazione (il resto del metodo) da chiamare quando il timer utilizzato dagli Task.Delayincendi.
MgSam,

1
Questa risposta è sbagliata a causa del sonno. Vedi la risposta accettata con wait Task.Delay (1000); che ha il comportamento corretto.
Jared Updike,

62

Mostrando le spiegazioni sopra in azione in un semplice programma 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 di esecuzione prolungata tramite TestAsyncAwaitMethods. Ciò ritorna immediatamente senza arrestare il thread corrente e viene immediatamente visualizzato il messaggio "Premere un tasto qualsiasi per uscire"
  2. Per tutto questo tempo, LongRunningMethodè in esecuzione in background. Una volta completato, un altro thread di Threadpool raccoglie questo contesto e visualizza il messaggio finale

Pertanto, non viene bloccato il thread.


"Premere un tasto qualsiasi per uscire ..." verrà mostrato in quale parte dell'output?
StudioX,

1
e a che serve (return 1)? è necessario?
StudioX,

1
@StudioX penso che debba avere un intero di ritorno
Kuba Do,

Penso che la return 1parte meriti qualche ulteriore spiegazione: la awaitparola chiave ti consente di restituire direttamente il tipo sottostante di Task<T>, rendendo così più facile adattare il tuo codice di uscita al mondo wait / async . Ma non è necessario restituire un valore, poiché è possibile restituire a Tasksenza specificare un tipo di ritorno, che sarebbe l'equivalente di un voidmetodo sincrono . Tieni presente che C # consente i async voidmetodi, ma dovresti evitare di farlo a meno che tu non stia affrontando i gestori di eventi.
Christiano Kiss,

41

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

Punto di un'attività asyncè lasciarlo eseguire in background senza bloccare il thread principale, come fare unDownloadFileAsync

System.Threading.Thread.Sleep non è qualcosa che si sta "facendo", dorme e quindi la riga successiva viene raggiunta dopo 5 secondi ...

Leggi questo articolo, penso che sia una grande spiegazione asynce awaitconcetto: http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx


3
Perché Sleep è un cattivo esempio, ma Download è un buon esempio. È come una specie di FooBar quando vedo Thread.Sleep Capisco che c'è un compito che richiede tempo. Penso che la sua domanda sia pertinente
Abdurrahim,

1
@Abdurrahim Thread.Sleepblocca il thread (il thread non può fare altro che rimanere inattivo), ma un metodo asincrono no. Nel caso di DownloadFileAsync, il thread può andare e fare qualcos'altro fino a quando non arriva una risposta dal server remoto. Un segnaposto migliore per "alcune attività che richiedono tempo" in un metodo asincrono è Task.Delay, dal momento che in realtà è asincrono.
Gabriel Luci,

@GabrielLuci la mia obiezione non riguarda Delay vs Sleep; La tua risposta sembra più una risposta di paglia; Se lo metti come un commento alla domanda che non avrei potuto obiettare, ma come risposta ha un odore più simile a una risposta di paglia. Penso che sia ancora bene usare l'asincrono, anche se tutte le chiamate che deve fare saranno bloccando le chiamate; Non invaliderà tutto lo scopo ... Anche tutto ciò che resterà sarà zucchero sintattico che conta come un caso valido,
Abdurrahim

1
Questa non era la mia risposta. Ma per affrontare il tuo punto: dipende dallo scopo del metodo. Se voleva solo un metodo da chiamare, ci riuscì. Ma in questo caso stava cercando di creare un metodo che funziona in modo asincrono. Lo ha fatto semplicemente usando la asyncparola chiave. Ma il suo metodo continuava a funzionare in modo sincrono e questa risposta spiegava perfettamente perché: perché non aveva effettivamente eseguito alcun codice asincrono. I metodi contrassegnati vengono asynccomunque eseguiti in modo sincrono fino a quando non sono awaitincompleti Task. In caso contrario await, il metodo viene eseguito in modo sincrono e il compilatore ti avviserà.
Gabriel Luci,

23

Ecco un programma di console rapida per chiarire a coloro che seguono. Il TaskToDometodo è il tuo metodo di lunga durata che desideri rendere asincrono. Far funzionare asincrono viene eseguito con il TestAsyncmetodo. Il metodo dei cicli di test esegue semplicemente le TaskToDoattività e le esegue in modo asincrono. Puoi vederlo nei risultati perché non si completano nello stesso ordine da un'esecuzione all'altra: al termine eseguono il reporting al thread dell'interfaccia utente della console. Semplicistico, ma penso che gli esempi semplicistici evidenzino 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);
        }
    }
}

22

Per l'apprendimento più veloce ..

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

  • Domanda introspezione (apprendimento): 1 min

  • Passa rapidamente attraverso la sintassi dello zucchero: 5 minuti

  • Condividi la confusione di uno sviluppatore: 5 minuti

  • Problema: modifica rapida di un'implementazione nel mondo reale del codice normale in codice asincrono: 2 minuti

  • Dove andare dopo?

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

In questa immagine, concentrati solo sul numero 6 (niente di più) inserisci qui la descrizione dell'immagine

Al passaggio n. 6: l'esecuzione si è fermata qui perché è rimasta senza lavoro. Per continuare ha bisogno di un risultato da getStringTask (tipo di funzione). Pertanto, utilizza un awaitoperatore per sospendere i suoi progressi e restituire il controllo (rendimento) al chiamante (di questo metodo in cui ci troviamo). La chiamata effettiva a getStringTask è stata effettuata in precedenza nel numero 2. Al n. 2 è stata promessa di restituire un risultato di stringa. Ma quando restituirà il risultato? Dovremmo (# 1: AccessTheWebAsync) effettuare nuovamente una seconda chiamata? Chi ottiene il risultato, # 2 (dichiarazione di chiamata) o # 6 (in attesa di dichiarazione)

Anche il chiamante esterno di AccessTheWebAsync () è in attesa ora. Quindi il chiamante in attesa di AccessTheWebAsync e AccessTheWebAsync sta aspettando GetStringAsync al momento. La cosa interessante è che AccessTheWebAsync ha lavorato un po 'prima di aspettare (# 4) forse per risparmiare tempo dall'attesa. La stessa libertà di multitasking è disponibile anche per il chiamante esterno (e per tutti i chiamanti della catena) e questo è il più grande vantaggio di questa cosa "asincrona"! Ti senti sincrono ... o normale ma non lo è.

Ricorda, il metodo è già stato restituito (n. 2), non può più essere restituito (nessuna seconda volta). Quindi come farà il chiamante a saperlo? Si tratta di Compiti! L'attività è stata passata. L'attività era in attesa (non metodo, non valore). Il valore verrà impostato in Attività. Lo stato dell'attività verrà impostato per il completamento. Il chiamante monitora solo l'attività (n. 6). Quindi 6 # è la risposta a dove / chi ottiene il risultato. Ulteriori letture per più tardi qui .

Domanda introspezione per motivi di apprendimento: 1 min

Modifichiamo un po 'la domanda:

Come e quando usare e ? asyncawait Tasks

Perché l'apprendimento Taskcopre automaticamente gli altri due (e risponde alla tua domanda)

Passa rapidamente attraverso la sintassi dello zucchero: 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 metodo Task-ified 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 già detto in attesa o asincrono? No. Chiama il metodo sopra e otterrai un'attività che puoi monitorare. Sai già cosa restituisce l'attività .. un numero intero.

  • Chiamare un'attività è leggermente complicato ed è allora che iniziano a comparire le parole chiave. Chiamiamo MethodTask ()

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

Stesso codice sopra aggiunto come immagine qui sotto: inserisci qui la descrizione dell'immagine

  1. Stiamo "aspettando" il completamento del compito. Quindi ilawait
  2. Poiché usiamo waitit, dobbiamo usare async(sintassi obbligatoria)
  3. MethodAsync con Asynccome prefisso (standard di codifica)

awaitè facile da capire ma i restanti due ( async, Async) potrebbero non essere :). Beh, dovrebbe avere molto più senso per il compilatore, ma leggi più avanti qui

Quindi ci sono 2 parti.

  1. Crea 'Task'
  2. Crea zucchero sintattico per chiamare l'attività ( await+async)

Ricorda, abbiamo avuto un chiamante esterno per AccessTheWebAsync () e neanche quel chiamante è risparmiato ... cioè ha bisogno anche dello stesso await+async. E la catena continua. Ma ci sarà sempre una Taska un'estremità.

Tutto bene, ma uno sviluppatore è stato sorpreso di vedere mancare il numero 1 (attività) ...

Condividi la confusione di uno sviluppatore: 5 minuti

Uno sviluppatore ha fatto un errore di non implementazione Taskma funziona ancora! Cerca di capire la domanda e solo la risposta accettata fornita qui . Spero che tu abbia letto e compreso appieno. Il riassunto è che potremmo non vedere / implementare "Task" ma è implementato da qualche parte in una classe genitore. Allo stesso modo, nel nostro esempio, chiamare un già costruito MethodAsync()è molto più semplice che implementare quel metodo con un Task( MethodTask()) noi stessi. La maggior parte degli sviluppatori ha difficoltà a orientarsi Tasksmentre converte un codice in uno asincrono.

Suggerimento: prova a trovare un'implementazione Async esistente (come MethodAsynco ToListAsync) per esternalizzare la difficoltà. Quindi dobbiamo solo occuparci di Async e attendere (che è facile e abbastanza simile al normale codice)

Problema: modifica rapida di un'implementazione nel mondo reale del codice normale in operazione asincrona: 2 minuti

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

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

vai tranquillo!

  1. Abbiamo installato il pacchetto nuget EntityFramework perché ha QueryableExtensions. O in altre parole esegue l'implementazione Async (attività), in modo da poter sopravvivere con codice semplice Asynce awaitin codice.
  2. namespace = Microsoft.EntityFrameworkCore

la linea del codice chiamante è stata modificata in questo modo

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

    Contract GetContract(int contractnumber)

    per

    async Task<Contract> GetContractAsync(int contractnumber)

  2. Anche il metodo di chiamata è stato interessato: è GetContractAsync(123456);stato chiamato comeGetContractAsync(123456).Result;

  3. L'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 Task personalizzata (yuk). Che tu sai come. Ancora facile! ..still yuk ..

Dove andare dopo? C'è un meraviglioso video rapido che potremmo guardare sulla conversione di chiamate sincrone in asincrone in ASP.Net Core , forse è probabilmente la direzione in cui andremmo dopo aver letto questo.


risposta fantastica! questo mi ha aiutato un sacco
cklimowski il

1
Bella risposta. Potresti voler risolvere un paio di piccole cose come: (a) menzione di ".Net framework 4.2" (non esiste una versione del genere che io conosca,) (b) alloggiamento in EntityFrameWork => EntityFramework
immitev

15

Tutte le risposte qui usano Task.Delay()o qualche altra asyncfunzione integrata. Ma ecco il mio esempio che non utilizza nessuna di queste asyncfunzioni:

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

2
Grazie! la prima risposta che in realtà funziona un po 'invece di aspettare.
Jeffnl,

grazie per aver mostrato task.Wait();e come può essere usato per evitare asincrono / attendere l'inferno: P
encoder

12

Questa risposta ha lo scopo di fornire alcune informazioni specifiche su ASP.NET.

Utilizzando asincrono / wait 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 vedono un gran numero di richieste simultanee all'avvio o presentano un carico eccessivo (in cui la concorrenza aumenta improvvisamente), rendere asincrone queste chiamate ai servizi Web aumenterà la capacità di risposta dell'applicazione. Una richiesta asincrona richiede lo stesso tempo per l'elaborazione di una richiesta sincrona. Ad esempio, se una richiesta effettua una chiamata al servizio Web che richiede due secondi per essere completata, la richiesta impiegherà due secondi se viene eseguita in modo sincrono o asincrono. Tuttavia, durante una chiamata asincrona, a un thread non viene impedito di rispondere ad altre richieste mentre attende il completamento della prima richiesta. Pertanto, le richieste asincrone impediscono l'accodamento delle richieste e la crescita del pool di thread quando vi sono molte richieste simultanee che invocano operazioni di lunga durata.


12

Asincrono e attendono una spiegazione semplice

Analogia semplice

Una persona può aspettare il loro treno mattutino. 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ò attendere il loro treno mattutino mentre fuma una sigaretta e poi beve il caffè. (Programmazione asincrona)

Cos'è la programmazione asincrona?

La programmazione asincrona è dove 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 asincrona?

Prefisso la parola chiave asincrona su un nome metodo come

async void DoSomething(){ . . .

consente al programmatore di utilizzare la parola chiave wait quando chiama attività asincrone. Questo è tutto.

Perché questo è importante?

In molti sistemi software il thread principale è riservato alle operazioni specificamente relative all'interfaccia utente. Se sto eseguendo 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 qualcosa sulla mia applicazione, sembrerà congelato poiché il mio thread principale è stato 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 Await?

Usa le parole chiave asincrone idealmente quando stai facendo qualcosa che non coinvolge l'interfaccia utente.

Supponiamo quindi che tu stia 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, poiché l'utente dell'applicazione deve continuare a interagire con il touchscreen mobile per disegnare belle immagini.

Come usi Async e Await

Seguendo l'esempio sopra, ecco alcuni pseudo codici su come scriverlo:

    //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();
    }

Note aggiuntive - Aggiornamento

Ho dimenticato di menzionare nelle mie note originali che in C # puoi aspettare solo metodi racchiusi in Attività. per esempio potresti aspettare questo metodo:

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

Non puoi attendere metodi che non sono compiti come questo:

async string FetchHelloWorld() {..

Sentiti libero di rivedere il codice sorgente per la classe Task qui .


4
Grazie per aver dedicato del tempo a scrivere questo.
Prashant,

10

Async / Await

In realtà Async / Await sono una coppia di parole chiave che sono solo zucchero sintattico per creare un callback 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]
        });
    }

Il codice sopra presenta diversi svantaggi. Gli errori non vengono trasmessi ed è difficile da leggere. Ma Async e Await entrano 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");

    }

Le chiamate in attesa devono essere in metodi asincroni. Questo ha alcuni vantaggi:

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

NOTA : Async e Await vengono utilizzati con chiamate asincrone per non effettuare queste. Devi usare Task Libary per questo, come Task.Run ().

Ecco un confronto tra le aspettative e nessuna attesa

Questa è la soluzione nessuna asincrona:

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

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

Puoi effettivamente chiamare un metodo asincrono senza la parola chiave wait, ma ciò significa che qualsiasi eccezione qui viene inghiottita 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. Quando si tratta di applicazioni asp.net o Windows, bloccare il thread principale a causa di una chiamata di rete è una cosa negativa. In tal caso, l'app non risponderà o si arresterà in modo anomalo.

Dai un'occhiata a ms docs per altri esempi.


9

Ad essere sincero, penso ancora che la migliore spiegazione sia quella sul futuro e le promesse su Wikipedia: http://it.wikipedia.org/wiki/Futures_and_promises

L'idea di base è che hai un pool separato di thread che eseguono le attività in modo asincrono. Quando lo si utilizza. L'oggetto promette comunque che eseguirà l'operazione in qualche momento e ti darà il risultato quando lo richiederai. Ciò significa che si bloccherà quando si richiede il risultato e non è terminato, ma altrimenti verrà eseguito nel pool di thread.

Da lì puoi ottimizzare le cose: alcune operazioni possono essere implementate in modo asincrono e puoi ottimizzare cose come file IO e comunicazione di rete raggruppando 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.

Puoi effettivamente implementare il modello futuro con rendimenti 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 giocare da solo, noterai che hai davvero bisogno del supporto linguistico se vuoi fare tutte le cose interessanti - che è esattamente quello che ha fatto Microsoft.


8

Guarda questo violino https://dotnetfiddle.net/VhZdLU (e miglioralo se possibile) per eseguire una semplice applicazione console che mostra gli usi di Task, Task.WaitAll (), asincronizza e attendi operatori nello stesso programma.

Questo violino dovrebbe chiarire il concetto del 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: inserisci qui la descrizione dell'immagine


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

3

A un livello superiore:

1) La parola chiave asincrona attiva l'attesa e questo è tutto. La parola chiave asincrona non esegue il metodo in un thread separato. Il metodo asincrono iniziale viene eseguito in modo sincrono fino a quando non viene atteso in un'attività che richiede tempo.

2) È possibile attendere un metodo che restituisce Task o Task di tipo T. Non è possibile attendere il metodo void asincrono.

3) Nel momento in cui si incontrano i thread principali in un'attività che richiede tempo o quando viene avviato il lavoro effettivo, il thread principale torna al chiamante del metodo corrente.

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

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

6) Una volta completata questa attività attesa, 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();
        }

    }
}

2

Il modo in cui ho capito che è anche, ci dovrebbe essere un terzo mandato aggiunto al mix: Task.

Async è solo un qualificatore che inserisci nel tuo metodo per dire che è un metodo asincrono.

Taskè il ritorno della asyncfunzione. Si esegue in modo asincrono.

Sei awaitun compito. Quando l'esecuzione del codice raggiunge questa linea, il controllo salta indietro al chiamante della funzione originale circostante.

Se invece si assegna il ritorno di una asyncfunzione (cioè Task) a una variabile, quando l'esecuzione del codice raggiunge questa riga, continua oltre quella riga nella funzione circostante mentre ilTask eseguito in modo asincrono.


1

li sta usando come spawn di thread in background per eseguire logiche di lunga durata?

Questo articolo MDSN: Programmazione asincrona con asincrono e wait (C #) lo spiega esplicitamente:

Le parole chiave asincrone e attendono non causano la creazione di thread aggiuntivi. I metodi asincroni non richiedono il multithreading perché un metodo asincrono non viene eseguito sul proprio thread. Il metodo viene eseguito nel contesto di sincronizzazione corrente e utilizza l'ora sul thread solo quando il metodo è attivo.


1

Nel codice seguente, il metodo HttpClient GetByteArrayAsync restituisce un'attività, getContentsTask. L'attività è una promessa di produrre l'array di byte effettivo quando l'attività è completa. L'operatore attende viene applicato a getContentsTask per sospendere l'esecuzione in SumPageSizesAsync fino al completamento di getContentsTask. Nel frattempo, il controllo viene restituito al chiamante di SumPageSizesAsync. Al termine di getContentsTask, l'espressione waitit restituisce un array 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

Di seguito è riportato il codice che legge il file Excel aprendo la finestra di dialogo e quindi utilizza asincrono e attende di eseguire in modo asincrono il codice che legge una per una riga da Excel e si lega 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

Le risposte qui sono utili come guida generale su waitit / async. Contengono anche alcuni dettagli su come cablare wait / async. Vorrei condividere con voi alcune esperienze pratiche che dovreste conoscere prima di utilizzare questo modello di progettazione.

Il termine "wait" è letterale, quindi qualsiasi thread tu lo chiami attenderà il risultato del metodo prima di continuare. Sul thread in primo piano , questo è un disastro . Il thread in primo piano ha l'onere di costruire la tua app, incluse viste, modelli di vista, animazioni iniziali e qualsiasi altra cosa che hai legato con quegli elementi. Quindi, quando aspetti il ​​thread in primo piano, interrompi l'app. L'utente attende e attende quando non sembra accadere nulla. Ciò fornisce un'esperienza utente negativa.

Puoi sicuramente 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 è disponibile all'indirizzo 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.


1
Da quello che ho capito, async / awaitè lo zucchero sintattico per i callback, non ha nulla a che fare con il threading. msdn.microsoft.com/en-us/magazine/hh456401.aspx È per codice associato senza CPU, ad es. in attesa di input o ritardo. Task.Rundovrebbe essere usato solo per il codice associato alla 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.Questo non è vero - forse intendevi Task.Wait ()? Quando lo usi await, imposta il resto del metodo come continuazione da eseguire quando tutto ciò che ti aspetti è completo. Esce dal metodo in cui è stato utilizzato, quindi il chiamante può continuare. Quindi quando la linea di attesa è effettivamente completa, termina il resto di quel metodo su un thread (di solito un thread di lavoro).
Don Cheadle,

@geometrikal, in sostanza, async/awaitriguarda la liberazione di thread .NET. Quando si awaitesegue un'operazione veramente asincrona (come File.WriteAsync di .NET), si sospende il resto del metodo utilizzato awaitin modo che il chiamante possa continuare e potenzialmente terminare il suo scopo. Non ci sono blocchi di thread o in attesa awaitdell'operazione -ed. Al termine dell'operazione await, il resto del async/awaitmetodo viene inserito in un thread ed eseguito (simile a un'idea di richiamata).
Don Cheadle,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.