C # async / await: Pedantry vs. il Debugger

4

Mi diverto con async e await e sembrano piuttosto intuitivi, ma alcune delle cose che sto leggendo su queste parole chiave non hanno senso per me. In effetti, alcuni di questi mi sembrano sbagliati, o almeno fuorvianti, anche se provengono direttamente da Microsoft. Considera il seguente codice:

namespace AsyncExample2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private async void button1_Click(object sender, EventArgs e)
        {   
            var t=new Task<int>(longRunningWork);
            t.Start(); 
            int r= await t;
            textBox1.Text += (r.ToString());
        }

        int longRunningWork()
        {
            Thread.Sleep(15000); 
            return (new Random()).Next(10);
        }
    }
}

Questo è da un'applicazione di esempio WinForms che ho creato con una GUI composta da un singolo pulsante e una singola casella di testo. Quando si preme il pulsante, 15 secondi dopo, una cifra casuale viene aggiunta al testo nella casella di testo. Durante questo periodo di 15 secondi, la GUI rimane interattiva, ad es. il modulo può essere massimizzato / minimizzato / spostato. Il pulsante può anche essere cliccato di nuovo.

Il modo più semplice in cui questo codice sembra contraddire ciò che Microsoft dice di attendere / async è che, secondo loro, la mia chiamata a t.Start () non dovrebbe essere necessario. Considera la primissima affermazione dell'articolo al link : "[i] n un metodo asincrono, le attività vengono avviate quando vengono create. " Questo non sembra essere il caso in questo particolare metodo async ( button1_Click ), sebbene l'abbia visto accadere in altri contesti.

In secondo luogo, ho letto molto di ciò che descriverei come tentativi aggressivi di differenziare il vecchio modello basato su thread del parallelismo e async / attendere , e , come il "Tutto nuovo!" le etichette applicate spesso agli imballaggi dei prodotti da parte dei reparti marketing aziendali, questi tentativi di differenziazione sembrano contenere alcune spinte ingiustificate.

Considera la frase che "le parole chiave async e await non provocano la creazione di thread aggiuntivi" (da link ). Questa non è la mia osservazione; se controllo Thread.CurrentThread.ManagedThreadId , posso vedere che longRunningWork () non è in esecuzione sul thread della GUI, sebbene button1_Click lo sia certamente. Inoltre, se faccio clic sul pulsante rapidamente in successione e ispeziono Thread.CurrentThread.ManagedThreadId alla chiamata a Thread.Sleep () , vedo vari ID thread gestiti diversi . E se provo ad aggiungere qualcosa a longRunningWork () che influenza la GUI (ad esempio, imposta textBox1.Text ), ottengo il tipico errore "chiamata cross-thread illegale" .

Qualcuno può aiutarmi a conciliare queste apparenti incoerenze? La mia impressione è che la prima incongruenza che evidenzi sia un esempio relativamente piccolo di sciatteria (anche se mi piacerebbe davvero sapere quando devo chiamare Start () manualmente).

La seconda incoerenza, sospetto, è solo pedanteria. Le persone che conoscono un po 'di async / await amano correggere i noobs che non possono parlare di queste parole chiave senza pensare in termini di thread. Capisco; è un modello diverso di parallelismo con un modo di pensare completamente diverso. Allo stesso tempo, tuttavia, è un oversell preciso dire che "le parole chiave async e await non provocano la creazione di thread aggiuntivi." Il debugger implica diversamente.

Suppongo che tecnicamente sia possibile che i thread che sto vedendo vengano catturati da una sorta di pool che esiste durante l'esecuzione del programma. Se questo è il caso, quindi, tecnicamente, niente viene creato da async / await . Questo mi sembra piuttosto banale, e non sono a conoscenza del fatto che i programmi WinForms abbiano un pool di thread in stile ASP.NET.

    
posta user1172763 13.08.2015 - 19:21
fonte

1 risposta

3

Potresti riscrivere il tuo codice in questo modo in un puro paradigma asincrono / atteso.

    private async void button1_Click(object sender, EventArgs e)
    {
        int r = await longRunningWork();
        textBox1.Text += (r.ToString());
    }

    private async Task<int> longRunningWork()
    {
        await Task.Delay(15000);
        return (new Random()).Next(10);
    }

Il fatto che un thread sia creato dipende dal CLR per decidere. Ciò che l'attende nel metodo button1_Click è che restituisce il controllo al thread chiamante di button1_Click (thread UI) che può fare altro lavoro. Il compilatore introduce il codice automatico ogni volta che attende di aggiungere qualche goto alla fine del metodo attendibile ogni volta che viene completato per continuare l'esecuzione in cui è stato lasciato.

La mia ipotesi qui perché penso che il tuo codice genera una nuova discussione è perché chiami Thread.Sleep (15000); che è una chiamata sincrona, quindi se CLR vuole ridare il controllo al thread dell'interfaccia utente, sa che deve generare un nuovo thread.

Anche il tuo metodo longRunningWork originale è sincrono quindi è necessario un nuovo thread.

    
risposta data 13.08.2015 - 23:31
fonte

Leggi altre domande sui tag