2013-10-09 13 views
12

Come strutturare il codice qui sotto in modo che venga richiamato il metodo asincrono?Come scrivere correttamente Parallel.Per metodi asincroni

Parallel.For(0, elevations.Count(), delegate(int i) 
{ 
    allSheets.AddRange(await BuildSheetsAsync(userID, elevations[i], includeLabels)); 
}); 
+0

Possibile duplicato di [Nesting await in Parallel.ForEach] (https://stackoverflow.com/questions/11564506/nesting-await-in-parallel-foreach) –

risposta

22

Parallel.For() non funziona bene con async metodi. Se non è necessario limitare il grado di parallelismo (vale a dire che stai bene con tutte le attività che eseguono allo stesso tempo), si può semplicemente avviare tutti i Task s e poi aspettare per loro di completare:

var tasks = Enumerable.Range(0, elevations.Count()) 
    .Select(i => BuildSheetsAsync(userID, elevations[i], includeLabels)); 
List<Bitmap> allSheets = (await Task.WhenAll(tasks)).SelectMany(x => x).ToList(); 
+0

Non sono in grado di aggiungere la raccolta di Elenco che richiede IEnnumer. come faccio a farlo funzionare? var task = Enumerable.Range (0, elevations.Length) .Select (i => BuildSheetsAsync (userID, elevations [i], includeLabels)); Elenco allSheets = new List (); allSheets.AddRange (attende Task.WhenAll (attività)); L'errore si verifica quando I allSheets.AddRange (attende Task.WhenAll (task)); –

+0

@ AlumCloud.Com Ah, non ho notato che 'BuildSheetsAsync()' restituisce già una raccolta. Se si desidera creare una singola raccolta da una raccolta di raccolte, è possibile utilizzare 'SelectMany()'. E per creare una lista da una collezione 'IEnumerable', usa' ToList() '. – svick

+0

Bingo! molte grazie. –

4

vi consiglio di dare un'occhiata a questa domanda ho chiesto un paio di giorni fa e finito-up me rispondere, in fondo ero alla ricerca di un metodo ForEach parallelo e asincrona.

Il metodo utilizza SemaphoreSlim per elaborare le cose in parallelo e accetta metodi asincroni come azione di input.

Si potrebbe anche voler dare un'occhiata ai due collegamenti che ho fornito alla fine della mia risposta, sono stati davvero utili per la realizzazione di tale comportamento e contengono anche un altro modo di farlo utilizzando uno Partitioner invece.

Personalmente, non mi piaceva il Parallel.For perché è una chiamata sincrona come spiegato nei collegamenti che ho dato; Volevo tutto 'asincrono' :-)

Eccolo: Asynchronously and parallelly downloading files

+1

+1, inoltre 'SemaphoreSlim' non è richiesto in il caso dell'OP. Potrei anche suggerire di usare 'CancellationTokenSource' con timeout invece di' Timer' object nel tuo codice. – Noseratio

2

il modo più semplice per invocare il metodo asincrono all'interno Parallel.For è prossimo:

Parallel.For(0, elevations.Count(), async i => 
{ 
    allSheets.AddRange(await BuildSheetsAsync(userID, elevations[i], includeLabels)); 
}); 

==============

MarioDS menzionato assolutamente ragione nel commento che in In tal caso potresti avere eccezioni inosservate. E questa è sicuramente una cosa molto importante che dovresti sempre tenere a mente e avere un accordo con i delegati asincroni.

In questo caso, se si ritiene di avere eccezioni, è possibile utilizzare il blocco try/catch all'interno del delegato. O in alcuni casi, se la situazione è buona, puoi iscriverti all'evento TaskScheduler.UnobservedTaskException.

+1

Questo porta a bug. I delegati asincroni restituiscono 'Task' e il costrutto Parallel non attende tale compito. Ciò significa che le eccezioni sono inosservate e non puoi essere sicuro dopo il 'Parallel.Per l'invocazione è stato eseguito che tutto il lavoro è stato effettivamente completato (né è possibile verificare facilmente). – MarioDS

Problemi correlati