Esecuzione di più compiti asincroni e in attesa che vengano completati tutti

Devo eseguire più attività asincrone in un’applicazione console e attendere che vengano completate tutte prima dell’ulteriore elaborazione.

Ci sono molti articoli là fuori, ma mi sembra di essere più confuso più leggo. Ho letto e capito i principi di base della libreria Task, ma mi manca chiaramente un collegamento da qualche parte.

Capisco che è ansible concatenare le attività in modo che avvengano dopo un altro completamento (che è praticamente lo scenario per tutti gli articoli che ho letto), ma voglio che tutte le mie attività vengano eseguite contemporaneamente e voglio sapere una volta sono tutti completati

Qual è l’implementazione più semplice per uno scenario come questo?

Entrambe le risposte non hanno menzionato l’attendibile Task.WhenAll :

 var task1 = DoWorkAsync(); var task2 = DoMoreWorkAsync(); await Task.WhenAll(task1, task2); 

La differenza principale tra Task.WaitAll e Task.WhenAll è che il primo bloccherà (simile all’utilizzo di Wait su una singola attività) mentre il secondo non sarà e potrà essere atteso, restituendo il controllo al chiamante fino al termine di tutte le attività.

Inoltre, la gestione delle eccezioni è diversa:

Task.WaitAll :

Almeno una delle istanze di Task è stata cancellata -oppure è stata generata un’eccezione durante l’esecuzione di almeno una delle istanze di Task. Se un’attività è stata annullata, AggregateException contiene una OperationCanceledException nella sua raccolta InnerExceptions.

Task.WhenAll :

Se una qualsiasi delle attività fornite viene completata in uno stato di errore, l’attività restituita verrà completata anche in uno stato Fault, in cui le eccezioni conterranno l’aggregazione dell’insieme di eccezioni non eseguite da ciascuna delle attività fornite.

Se nessuna delle attività fornite era in errore ma almeno una di esse era stata annullata, l’attività restituita terminerà nello stato Annullato.

Se nessuna delle attività ha avuto esito negativo e nessuna delle attività è stata annullata, l’attività risultante terminerà nello stato RanToCompletion. Se l’array fornito / enumerabile non contiene attività, l’attività restituita passerà immediatamente a uno stato RanToCompletion prima di essere restituito al chiamante.

Potresti creare molte attività come:

 List TaskList = new List(); foreach(...) { var LastTask = new Task(SomeFunction); LastTask.Start(); TaskList.Add(LastTask); } Task.WaitAll(TaskList.ToArray()); 

L’opzione migliore che ho visto è il seguente metodo di estensione:

 public static Task ForEachAsync(this IEnumerable sequence, Func action) { return Task.WhenAll(sequence.Select(action)); } 

Chiamalo così:

 await sequence.ForEachAsync(item => item.SomethingAsync(blah)); 

O con un lambda asincrono:

 await sequence.ForEachAsync(async item => { var more = await GetMoreAsync(item); await more.FrobbleAsync(); }); 

Vuoi concatenare i Task o possono essere richiamati in modo parallelo?

Per il concatenamento
Fai qualcosa del genere

 Task.Run(...).ContinueWith(...).ContinueWith(...).ContinueWith(...); Task.Factory.StartNew(...).ContinueWith(...).ContinueWith(...).ContinueWith(...); 

e non dimenticare di controllare l’istanza di Task precedente in ogni ContinueWith in quanto potrebbe essere errata.

Per il modo parallelo
Il metodo più semplice che ho trovato: Parallel.Invoke Altrimenti c’è Task.WaitAll o puoi anche usare WaitHandle s per fare un conto alla rovescia per azzerare le azioni rimaste (aspetta, c’è una nuova class: CountdownEvent ), o …

È ansible utilizzare WhenAll che restituisce un’attività attendibile o WaitAll che non ha alcun tipo di WaitAll e bloccherà l’ulteriore esecuzione del codice simulare a Thread.Sleep fino a quando tutte le attività non saranno completate, annullate o in errore.

inserisci la descrizione dell'immagine qui

Esempio

 var tasks = new Task[] { await TaskOperationOne(), await TaskOperationTwo() }; Task.WaitAll(tasks); // or await Task.WhenAll(tasks); 

Se si desidera eseguire le attività in un ordine pratico, è ansible ottenere ispirazione da questo esperto.

Ecco come faccio con un array Func <> :

 var tasks = new Func[] { () => myAsyncWork1(), () => myAsyncWork2(), () => myAsyncWork3() }; await Task.WhenAll(tasks.Select(task => task()).ToArray()); //Async Task.WaitAll(tasks.Select(task => task()).ToArray()); //Or use WaitAll for Sync