2015-07-18 5 views
10

Quasi la risposta di ogni tanto su questo argomento, afferma che:async all'interno di un codice LINQ - Chiarimento?

LINQ doesn't work perfectly with async

anche:

I recommend that you not think of this as "using async within LINQ"

Ma nel libro di Stephen v'è un campione per :

Problema: Hai una raccolta di attività per aspettare, e si vuole fare un po ' lavorazione su ogni attività dopo aver completato. Tuttavia, si desidera eseguire l'elaborazione per ciascuna non appena completata, non in attesa di una qualsiasi delle altre attività.

Una delle soluzioni consigliate era:

static async Task<int> DelayAndReturnAsync(int val) 
{ 
await Task.Delay(TimeSpan.FromSeconds(val)); 
return val; 
} 

// This method now prints "1", "2", and "3". 
static async Task ProcessTasksAsync() 
{ 
// Create a sequence of tasks. 
Task<int> taskA = DelayAndReturnAsync(2); 
Task<int> taskB = DelayAndReturnAsync(3); 
Task<int> taskC = DelayAndReturnAsync(1); 
var tasks = new[] { taskA, taskB, taskC }; 
var processingTasks = tasks.Select(async t => 
    { 
    var result = await t; 
    Trace.WriteLine(result); 
    }).ToArray(); 

// Await all processing to complete 
await Task.WhenAll(processingTasks); 

} 

Domanda # 1:

non capisco perché adesso async all'interno di una dichiarazione LINQ - funziona. Non abbiamo semplicemente detto "non pensare di utilizzare async all'interno di LINQ"?

Domanda # 2:

Quando il controllo raggiunge il await t qui - Che cosa è realmente accadere? Il controllo lascia il metodo ProcessTasksAsync? o lascia il metodo anonimo e continua l'iterazione?

+0

Quel "don "Penso a ..." non era un imperativo. Nessuno dei testi che hai citato dice che async non funzionerà con Linq - solo che non è una corrispondenza perfetta. –

+0

Una buona lettura di Stephen Toub, se non l'hai ancora incontrato: [Task, Monads e LINQ] (http://blogs.msdn.com/b/pfxteam/archive/2013/04/03/tasks -monads-e-linq.aspx). – Noseratio

risposta

8

Non capisco perché ora async all'interno di una istruzione LINQ - funziona. Non abbiamo appena detto "non pensare di usare async all'interno di LINQ"?

asyncprincipalmente non funziona con LINQ perché IEnumerable<T> estensioni non sempre dedurre il tipo delegato correttamente e rinviare a Action<T>. Non hanno una conoscenza particolare della classe Task. Ciò significa che il delegato asincrono effettivo diventa async void, che non è corretto. Nel caso di Enumerable.Select, abbiamo un sovraccarico che restituisce un Func<T> (che a sua volta sarà Func<Task> nel nostro caso), che è equivalente a async Task, quindi funziona bene per casi d'uso asincroni.

Quando il controllo raggiunge l'attesa t qui - Che cosa è realmente accaduto? Il controllo lascia il metodo ProcessTasksAsync?

No, non è così. Enumerable.Select riguarda la proiezione di tutti gli elementi nella sequenza.Ciò significa che per ogni elemento nella raccolta, await t, che restituirà il controllo all'iteratore, che continuerà a ripetere tutti gli elementi. Ecco perché in seguito devi await Task.WhenAll, per garantire che tutti gli elementi abbiano completato l'esecuzione.

3

Domanda 1:

La differenza è che ciascun compito è continuato con elaborazione aggiuntiva che è: Trace.WriteLine(result);. Nel link che hai indicato, quel codice non cambia nulla, crea solo un sovraccarico di attesa e avvolgimento con un'altra attività.

Domanda 2:

Quando il controllo raggiunge il attendono t qui - che cosa è realmente successo?

Si attende per il risultato del compito ProcessTasksAsync 's, quindi continuare con Trace.WriteLine(result);. Possiamo dire che il controllo lascia il metodo ProcessTasksAsync quando abbiamo il risultato e l'elaborazione è ancora all'interno del metodo anonimo.

Alla fine, abbiamo await Task.WhenAll(processingTasks); che attendere per tutte le attività, tra cui l'ulteriore elaborazione (Trace.WriteLine(result);) per completare prima di continuare, ma ogni attività non aspettano per gli altri per continuare l'esecuzione: Trace.WriteLine(result);