2016-06-06 38 views
6

Stavo leggendo su async/await parole chiave e ho letto che:async await: il thread principale è sospeso?

Quando il flusso della logica raggiunge la attendono motivo, il thread chiamante è sospesa fino a quando la chiamata viene completata.

Beh, ho creato un semplice windows forms application, collocate due etichette, un pulsante e una casella di testo e ho scritto il codice:

 private async void button1_Click(object sender, EventArgs e) 
     { 
      label1.Text = Thread.CurrentThread.ThreadState.ToString(); 
      button1.Text = await DoWork(); 
      label2.Text = Thread.CurrentThread.ThreadState.ToString(); 
     } 

     private Task<string> DoWork() 
     { 
      return Task.Run(() => { 
       Thread.Sleep(10000); 
       return "done with work"; 
      });    
     } 

Quello che non capisco è che quando clicco il l'etichetta1 avrà il testo Running e l'etichetta avrà lo stesso testo solo dopo 10 secondi, ma in questi 10 secondi ho potuto inserire il testo nella mia casella di testo, quindi sembra che il thread principale sia in esecuzione ...

Quindi, come funziona async/await?

ecco un "screenshot" dal libro: enter image description here

saluti

+0

Non si sta utilizzando 'async' /' await' nell'esempio e si esegue su un thread separato ('Task.Run' viene eseguito dal pool di thread). – Guvante

+0

Ma perché il testo label2 viene impostato dopo 10 secondi? questo significa che aspetto 10 secondi sul thread principale e perché in questi 10 secondi sono in grado di inserire del testo nella mia casella di testo? –

+0

Domanda ancora non chiara .. Inoltre non riesco a trovare la connessione tra 'DoWork' e l'evento del pulsante, inoltre non hai esposto il metodo' GetText'. Inoltre, "lo stesso testo solo dopo 10 secondi" è un po 'torbido. "Sono stato in grado di inserire il testo nella mia casella di testo" non si è mai limitati a questa opzione. Si prega di modificare e spiegare il problema in modo più dettagliato. –

risposta

15

Ho letto che: Quando il flusso della logica raggiunge la attendono gettone, il thread chiamante viene sospeso fino al completamento chiamata.

Dove hai letto quell'assurdità? O c'è un contesto in cui non stai citando, o dovresti smettere di leggere qualsiasi testo che lo contenga. Il punto di attesa è quello di fare il di fronte a di quello. Il punto di attesa è per mantenere il thread corrente facendo un lavoro utile mentre il task asincrono è in volo.

UPDATE: Ho scaricato il libro si fa riferimento. Assolutamente tutto in quella sezione è sbagliato. Getta via questo libro e compra un libro migliore.

Quello che non capisco è che quando clicco sul pulsante, l'etichetta1 avrà il testo in esecuzione e l'etichetta avrà lo stesso testo solo dopo 10 secondi, ma in questi 10 secondi ho potuto inserire il testo nella mia casella di testo, quindi sembra che il thread principale sia in esecuzione ...

Questo è corretto. Ecco cosa succede:

 label1.Text = Thread.CurrentThread.ThreadState.ToString(); 

Il testo è impostato.

 button1.Text = await DoWork(); 

Un mucchio di cose succede qui. Cosa succede prima? DoWork viene chiamato. Che cosa fa?

 return Task.Run(() => { Thread.Sleep(10000); 

Si afferra un filo fuori del pool di thread, mette quel filo di dormire per dieci secondi, e restituisce un compito che rappresenta il "lavoro" viene fatto da quel thread.

Ora siamo di nuovo qui:

 button1.Text = await DoWork(); 

Abbiamo un compito in mano. Attendi prima controlla l'attività per vedere se è già completa. Non è. Quindi registra il resto di questo metodo come la continuazione dell'attività. Quindi torna al chiamante.

Ehi, qual è il suo chiamante? Come siamo arrivati ​​comunque?

Alcuni codice chiamato questo gestore di eventi; era il ciclo degli eventi che sta elaborando i messaggi di Windows. Ha visto un pulsante cliccato e inviato al gestore dei clic, che è appena tornato.

Ora cosa succede? Il ciclo degli eventi continua a funzionare. La tua interfaccia utente continua a funzionare bene, come hai notato. Alla fine quel thread spunta dieci secondi e viene attivata la continuazione dell'attività. Cosa fa?

che i posti di un messaggio nella coda di Windows che dice "è necessario eseguire il resto di quel gestore di eventi ora;. Ho il risultato che stavi cercando"

il ciclo degli eventi thread principale arriva alla fine a quel messaggio.Così il gestore di eventi riprende da dove si era interrotto:

 button1.Text = await DoWork(); 

La attendono ora estrae il risultato dal compito, lo memorizza nel testo del pulsante, e ritorna al ciclo di eventi.

+0

Ho allegato uno "screenshot" dal libro ... Forse ho sbagliato ... Quindi, per farla breve: il thread dell'interfaccia utente continua a funzionare, l'esecuzione del codice è sospesa quando la parola chiave await è soddisfatta nel codice e continua quando il compito è finito, giusto? –

+5

@BudaGavril: Il libro è completamente e completamente sbagliato dall'inizio alla fine nella sezione async-await. L'autore ha completamente frainteso. Buttare via questo libro e ottenere un libro migliore. –

+0

@BudaGavril: corretto: l'esecuzione del * gestore di eventi * viene sospesa quando il comando 'await' restituisce il controllo al chiamante e viene ripreso in un momento successivo al completamento dell'attività. L'esecuzione del * UI thread * continua come sempre. –

0

async/await crea una macchine di stato che gestisce la prosecuzione per voi. Un equivalente molto approssimativa (senza un sacco di caratteristiche) è un metodo di continuazione esplicito, ad esempio:

private void button1_Click_Continuation(string value) 
{ 
    button1.Text = value; 
    label2.Text = Thread.CurrentThread.ThreadState.ToString(); 
} 

private void button1_Click(object sender, EventArgs e) 
{ 
    label1.Text = Thread.CurrentThread.ThreadState.ToString(); 
    DoWork(button1_Click_Continuation); 
} 

private void DoWork(Action<string> continuation) 
{ 
    Task.Run(() => { 
     Thread.Sleep(10000); 
     continuation("done with work"); 
    }); 
} 

noti che utilizzi ancora Task.Run di eseguire su un thread separato. Si noti che questa versione non ha alcun modo di seguire il progresso (mentre l'originale potrebbe cambiare il valore di ritorno di button1_Click a Task per vedere se è stato completato o meno).

Oltre alla trasformazione di cui sopra, il sistema è in grado di stabilire se un Task avviato sul thread dell'interfaccia utente e i marshalls di nuovo sul thread dell'interfaccia utente per assicurarsi che tutto funzioni come previsto. Questa è probabilmente la cosa principale che non hai realizzato, ma l'espansione nell'aspetto della macchina a stati spiega a volte cosa significa veramente asyc/await (invece di lasciarlo come mistico).

+0

Quindi stai dicendo che il codice dell'evento click viene eseguito su un altro thread? Perché questo spiegherebbe perché sono stato in grado di inserire del testo nella mia casella di testo. Ma che ne dici di aggiornare l'interfaccia utente da un thread secondario? C'è qualche documentazione che dice che posso aggiornare l'interfaccia utente senza problemi solo da metodi asincroni (senza altre soluzioni), il che significa che stai aggiornando l'interfaccia utente da un thread secondario? –

+2

@BudaGavril: No, ** assolutamente non **. Il codice dall'evento click viene eseguito sul thread dell'interfaccia utente; è un gestore di eventi dell'interfaccia utente! L'attesa * restituisce il controllo al ciclo di messaggi *; è per questo che l'interfaccia utente non si blocca. Potresti ** non ** aggiornare l'interfaccia utente da un secondo thread; devi ** solo ** aggiornare l'interfaccia utente dal thread dell'interfaccia utente. Await rende più semplice farlo perché consente di esprimere più facilmente flussi di lavoro asincroni ** che rimangono sul thread dell'interfaccia utente **, o in cui le continuazioni * vengono automaticamente sottoposte a marshalling *. –

+1

@BudaGavril: Questa risposta è fondamentalmente corretta, ma l'ultimo paragrafo è * estremamente importante *. La chiamata a 'continuation' in' Task.Run' non invoca direttamente la continuazione; fa sì che la continuazione ritorni sul thread dell'interfaccia utente, a cui appartiene. Si noti che in un'applicazione * console *, questo non è vero; la continuazione potrebbe essere programmata su un thread di lavoro. –

0

Scrivendo await, si sta dicendo - si prega di interrompere l'esecuzione del metodo a questo punto, attendere DoWork per finire e solo poi continuare.

Asynchronous Programming with async and await (C#) in cosa avviene negli sezione Metodo asincrono ha una spiegazione passo passo, con un quadro di ciò che avviene in un metodo async.

Una spiegazione ancora migliore è a await (C# reference). Dai un'occhiata ai commenti per WaitSynchronously e WaitAsynchronouslyAsync qui sotto.

L'articolo afferma inoltre (enfasi mia):

L'operatore await viene applicato a un'attività in un metodo asincrono di sospendere l'esecuzione del metodo finché il compito atteso completata. L'attività rappresenta il lavoro in corso.

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"; 
} 
+0

quindi il metodo attende sospende l'esecuzione del metodo, ma il thread principale (UI) è ancora in esecuzione?Quindi "Quando il flusso di logica raggiunge il token di attesa, il thread chiamante viene sospeso fino al completamento della chiamata." è solo vero vero? Voglio dire che non il thread è sospeso, ma solo l'esecuzione del codice dal metodo? –

+1

@BudaGavril: await sospende l'esecuzione del metodo mediante (1) la registrazione del resto del metodo come la continuazione dell'attività e (2) il ritorno. Tutto questo lavoro è fatto sul thread dell'interfaccia utente. Il thread dell'interfaccia utente * non * smette di funzionare. L'affermazione non è * parzialmente * vera; è completamente falso dall'inizio alla fine. La funzione di 'await' è quella di * prevenire * il blocco dell'interfaccia utente dal blocco, per non farlo bloccare. –