Domanda

Non vedo la differenza tra C # 's (e del VB) nuove funzionalità asincroni, e .NET 4.0 di Task Parallel Libreria . Prendiamo, per esempio, il codice di Eric Lippert da qui :

async void ArchiveDocuments(List<Url> urls) {
    Task archive = null;
    for(int i = 0; i < urls.Count; ++i) {
        var document = await FetchAsync(urls[i]);
        if (archive != null)
            await archive;
        archive = ArchiveAsync(document);
    }
}

Sembra che la parola await sta servendo due scopi diversi. La prima occorrenza (FetchAsync) sembra voler dire, "Se viene utilizzato questo valore più avanti nel metodo e il suo compito non è finito, attendere fino al completamento prima di continuare." la seconda istanza (archive) sembra voler dire, "Se questo compito non è ancora finito, attesa ora fino al completamento." Se mi sbaglio, per favore correggetemi .

Non potrebbe altrettanto facilmente essere scritto come questo?

void ArchiveDocuments(List<Url> urls) {
    for(int i = 0; i < urls.Count; ++i) {
        var document = FetchAsync(urls[i]);       // removed await
        if (archive != null)
            archive.Wait();                       // changed to .Wait()
        archive = ArchiveAsync(document.Result);  // added .Result
    }
}

Ho sostituito il primo await con un Task.Result dove è effettivamente necessario il valore, e il secondo con await Task.Wait(), dove l'attesa è effettivamente in corso. La funzionalità è (1) già implementato, e (2) molto più vicino semanticamente a ciò che sta realmente accadendo nel codice.

mi rendo conto che un metodo async viene riscritto come una macchina a stati, simile a iteratori, ma anche io non vedo cosa i benefici che porta. Qualsiasi codice che richiede un altro thread per funzionare (come lo scaricamento) sarà ancora bisogno di un altro thread, e qualsiasi codice che non lo fa (come ad esempio la lettura da un file) potrebbe ancora utilizzare il TPL di lavorare con solo un singolo thread.

Sono ovviamente manca qualcosa di enorme qui; Qualcuno può aiutarmi a capire questo un po 'meglio?

È stato utile?

Soluzione

Credo che l'equivoco nasce qui:

  

Sembra che la parola attendono sta servendo due scopi diversi. La prima occorrenza (FetchAsync) sembra voler dire, "Se questo valore viene utilizzato in seguito nel metodo e il suo compito non è finito, attendere fino al completamento prima di continuare." La seconda istanza (archivio) sembra voler dire, "Se questo compito non è ancora finito, aspetta questo momento fino al completamento." Se mi sbaglio, per favore correggetemi.

Questo è in realtà del tutto errata. Entrambi questi hanno lo stesso significato.

Nel primo caso:

var document = await FetchAsync(urls[i]);

Quello che succede qui, è che il runtime dice "Start chiamando FetchAsync, per poi tornare al punto di esecuzione corrente al filo chiamare questo metodo." Non ci sono "in attesa" qui - invece, l'esecuzione torna al contesto di sincronizzazione di chiamata, e le cose tenere sfornare. Ad un certo punto nel futuro, Task FetchAsync completerà, ea quel punto, questo codice riprenderà sul contesto di sincronizzazione del thread chiamante, e si verificherà l'istruzione successiva (assegnando la variabile del documento).

L'esecuzione poi proseguire fino alla seconda chiamata attendono - e in quel momento, la stessa cosa accadrà - se il Task<T> (filmati) non è completa, l'esecuzione sarà rilasciato al contesto di chiamata - in caso contrario, l'archivio sarà insieme .

Nel secondo caso, le cose sono molto diverse - qui, si sta esplicitamente bloccando, il che significa che il contesto di sincronizzazione chiamando non potrà mai avere la possibilità di eseguire qualsiasi codice fino a quando le intere Completa metodo. Certo, c'è ancora asincronia, ma l'asincronia è completamente contenuti in questo blocco di codice -. Nessun codice al di fuori di questo codice incollato accadrà su questo thread fino a quando tutti i tuoi Completa codice

Altri suggerimenti

C'è una differenza enorme:

blocchi Wait(), await Non blocca. Se si esegue la versione asincrona del ArchiveDocuments() sul tuo thread GUI, l'interfaccia grafica rimarrà reattivo, mentre le operazioni che vanno a prendere e l'archiviazione sono in esecuzione. Se si utilizza la versione TPL con Wait(), la vostra interfaccia grafica sarà bloccato.

Si noti che async riesce a farlo senza introdurre le discussioni - in corrispondenza del punto del await, il controllo viene semplicemente restituito al ciclo di messaggi. Una volta al compito aspettava ha completato, il resto del metodo (seguito) è accodato sul ciclo di messaggi e il filo GUI continua a funzionare ArchiveDocuments dove si è interrotta.

Anders bollito giù ad una risposta molto succinta nel Canale 9 in diretta intervista che ha fatto. Lo consiglio vivamente

Il nuovo Asincrono e le parole chiave attendono consentono di Orchestrate concorrenza nelle applicazioni. In realtà non introducono alcuna concorrenza per la vostra applicazione.

TPL e più specificamente Task è un modo è possibile utilizzare effettivamente eseguire operazioni contemporaneamente. La nuova parola chiave async e await consentono di composizione queste operazioni simultanee in un "sincrono" o "lineare" della moda.

Così si può ancora scrivere un flusso lineare di controllo nei vostri programmi, mentre l'elaborazione vera e propria può o non può accadere in concomitanza. Quando calcolo accade contemporaneamente, await e asincrone consentono di composizione queste operazioni.

La capacità di trasformare il flusso del programma di controllo in una macchina a stati è ciò che rende queste nuove parole chiave intresting. Pensate a come Controllo cedendo , piuttosto che valori.

questo canale 9 video di Anders parlando la nuova funzione.

Il problema qui è che la firma di ArchiveDocuments è fuorviante. Ha un ritorno esplicito void ma in realtà il ritorno è Task. A me senza implica sincrona in quanto non v'è alcun modo per "aspettare" che finisca. Si consideri la firma alternativa della funzione.

async Task ArchiveDocuments(List<Url> urls) { 
  ...
}

Per me, quando è scritto in questo modo la differenza è molto più evidente. La funzione ArchiveDocuments non è uno che completa in modo sincrono, ma si concluderà più tardi.

La chiamata alla FetchAsync() sarà ancora bloccare fino al completamento (a meno che una dichiarazione entro le chiamate await?) La chiave è che il controllo viene restituito al chiamante (perché il metodo ArchiveDocuments stesso viene dichiarato come async). Quindi il chiamante può continuare felicemente logica di elaborazione dell'interfaccia utente, rispondere a eventi, ecc

Quando si completa l'FetchAsync(), interrompe il chiamante per terminare il ciclo. Colpisce ArchiveAsync() e blocca, ma ArchiveAsync() probabilmente solo crea una nuova attività, lo avvia, e restituisce il compito. Questo permette al secondo ciclo per iniziare, mentre l'attività sta elaborando.

Il secondo ciclo colpisce FetchAsync() e blocchi, di restituire il controllo al chiamante. Quando si completa l'FetchAsync(), si interrompe di nuovo al chiamante di continuare l'elaborazione. E poi colpisce await archive, che restituisce il controllo al chiamante fino a quando il Task creato nel ciclo 1 completa. Una volta che il compito è completo, il chiamante viene di nuovo interrotto, e il secondo ciclo chiama ArchiveAsync(), che ottiene un compito iniziato e inizia ciclo 3, ripetere il fino alla nausea .

La chiave è di restituire il controllo al chiamante mentre i sollevatori pesanti sono in esecuzione.

La parola chiave await non introduce concorrenza. E 'come la parola chiave snervamento, si dice al compilatore di ristrutturare il codice in lambda controllato da una macchina a stati.

Per vedere quale codice await assomiglierebbe senza 'await' vedere questo ottimo link: http://blogs.msdn.com/b/windowsappdev/archive/2012/04/24/diving-deep-with-winrt-and-await aspx

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top