2015-06-13 4 views
7

Sto cercando di utilizzare Task.WaitAll in un elenco di attività. La cosa è che i compiti sono un lambda asincrono che si rompe Tasks.WaitAll come non aspetta mai.Task.Factory.StartNew con lambda async e Task.WaitAll

Ecco un blocco di codice di esempio:

List<Task> tasks = new List<Task>(); 
tasks.Add(Task.Factory.StartNew(async() => 
{ 
    using (dbContext = new DatabaseContext()) 
    { 
     var records = await dbContext.Where(r => r.Id = 100).ToListAsync(); 
     //do long cpu process here... 
    } 
} 
Task.WaitAll(tasks); 
//do more stuff here 

Questo non aspetta a causa della lambda asincrona. Quindi, come dovrei aspettare le operazioni di I/O nel mio lambda?

+0

Qual è il punto di partenza un'attività su un altro thread se la prima cosa che fai dopo l'avvio è un blocco sulla chiamata 'Task.WaitAll'? Otterrai prestazioni migliori eliminando 'ToListAsync' e rendendolo semplicemente' ToList' e eseguendolo in modo sincrono. (o se si desidera utilizzare 'ToListAsync', allora è necessario utilizzare async fino allo stack delle chiamate. –

risposta

10

Task.Factory.StartNew non riconosce async delegati come non c'è un sovraccarico che accetta una funzione che restituisce un Task.

Questo, più altri motivi (vedi StartNew is dangerous) è il motivo per cui si dovrebbe utilizzare Task.Run qui:

tasks.Add(Task.Run(async() => ... 
+0

Questo ha funzionato ... una specie. Non sono sicuro del problema ora, ma sto ottenendo un'eccezione annullata da un'attività che tenta di restituisce una stringa Sembra un altro problema quindi inizierò un nuovo thread SO. Grazie. –

+2

Troppo male Task.run non ti dà 'TaskCreationOptions' –

+0

' Task.Factory.StartNew (Funzione Func ' sembra essere disponibile in .NET Standard 1.6 - si potrebbe dover "Scartare()" il risultato 'compito' sebbene :) – urbanhusky

-1

è necessario utilizzare il metodo Task.ContinueWith. Ti piace questa

List<Task> tasks = new List<Task>(); 
tasks.Add(Task.Factory.StartNew(() => 
{ 
    using (dbContext = new DatabaseContext()) 
    { 
     return dbContext.Where(r => r.Id = 100).ToListAsync().ContinueWith(t => 
      { 
       var records = t.Result; 
       // do long cpu process here... 
      }); 
     } 
    } 
} 
0

Si può fare in questo modo.

void Something() 
    { 
     List<Task> tasks = new List<Task>(); 
     tasks.Add(ReadAsync()); 
     Task.WaitAll(tasks.ToArray()); 
    } 

    async Task ReadAsync() { 
     using (dbContext = new DatabaseContext()) 
     { 
      var records = await dbContext.Where(r => r.Id = 100).ToListAsync(); 
      //do long cpu process here... 
     } 
    } 
+0

Questa era quasi una buona risposta. Non dovresti bloccare su 'Task.WaitAll' se stai usando' async' perché puoi facilmente deadlock. –

+0

WaitAll è ciò che Jacob voleva fare in base alla domanda. – hebinda

9

Questo non aspetta a causa della lambda asincrona. Quindi come dovrei attendere operazioni di I/O nella mia lambda?

Il motivo Task.WaitAll non attendere il completamento dei lavori IO presentato dal lambda asincrona è perché Task.Factory.StartNew restituisce in realtà un Task<Task>. Poiché l'elenco è un List<Task> (e Task<T> deriva da Task), si attende l'attività esterna avviata da StartNew, mentre ignorando quella interna creata dal lambda async. Questo è il motivo per cui si dice che Task.Factory.StartNew è pericoloso per quanto riguarda asincrono.

Come si può risolvere questo? Si potrebbe chiamare esplicitamente Task<Task>.Unwrap() al fine di ottenere il compito interno:

List<Task> tasks = new List<Task>(); 
tasks.Add(Task.Factory.StartNew(async() => 
{ 
    using (dbContext = new DatabaseContext()) 
    { 
     var records = await dbContext.Where(r => r.Id = 100).ToListAsync(); 
     //do long cpu process here... 
    } 
}).Unwrap()); 

O come altri hanno detto, si potrebbe chiamare Task.Run invece:

tasks.Add(Task.Run(async() => /* lambda */); 

Inoltre, dal momento che si vuole essere fare le cose giuste, 'll desidera utilizzare Task.WhenAll, perché è in modo asincrono waitable, invece di Task.WaitAll che sincronicamente blocchi:

await Task.WhenAll(tasks);