2015-06-04 16 views
5

Ho il seguente codice (semplificato):nidificati Async/attendono Non sembra essere Scaling

public async Task GetData(DomainObject domainObject, int depth) 
{ 
    // This async operation is really quick, and there's usually like five. 
    IEnumerable<TierOne> tierOnes = await domainObject.GetTierOnesAsync(); 

    var tierOneTasks = tierOnes.Select(async tierOne => 
    { 
    // This async operation is really quick and there's usually like three. 
    IEnumerable<TierTwo> tierTwos = await tierOne.GetTierTwosAsync(); 

    if (depth <= TierTwoDepth) 
     return; 

    var tierTwoTasks = tierTwos.Select(async tierTwo => 
    { 
     // This async operation is usually fast, and there's usually >= 100. 
     IEnumerable<TierThree> tierThrees = await tierTwo.GetTierThreesAsync(); 

     if (depth <= TierThreeDepth) 
     return; 

     var tierThreeTasks = tierThrees.Select(async tierThree => 
     { 
     // This async operation is SLOW, and there's usually.. 50? 
     await tierThree.GetTierFoursAsync(); 
     }); 

     await Task.WhenAll(tierThreeTasks.ToArray()); 
    }); 

    await Task.WhenAll(tierTwoTasks.ToArray()); 
    }); 

    await Task.WhenAll(tierOneTasks.ToArray()); 
} 

base al largo di quello che ho visto, non sembra essere scalare molto bene. Tutte le operazioni di Async sono operazioni "true async", il che significa che sono tutti I/O.

Sto usando Async/Attendere in modo errato per questo scenario? Basandomi sulle mie attuali osservazioni, non è il ridimensionamento a ciò che mi aspetterei. TPL DataFlow sarebbe la mia soluzione?

+0

"sembra che si stia adattando molto bene" è un errore di battitura e si mette un "not" in là? E se sì, scalando in che modo, ti aspetti che finisca più velocemente o semplicemente non carichi tanto sul sistema? Come stai testando il ridimensionamento? –

+2

Si stanno utilizzando molti 'IEnumerables' come valori di ritorno asincrono. Sei sicuro che l'esecuzione differita non interferisca con la tua presunta parallelizzazione? – nvoigt

+0

@ScottChamberlain Sì, quello era un errore di battitura. Mi aspetto che finisca più velocemente. Capisco che arriverà alla stessa velocità con cui verrà elaborata la parte ricevente delle mie operazioni asincrone. Sembra che se spoolare 1500 attività, mi aspetterei che solo 3 o 4 siano bloccati nella parte 'Await' della macchina a stati. (Scusa se questo non ha senso. È tardi e sono molto assonnato.) – Cameron

risposta

0

Per una chiamata singola a GetData, le chiamate asincrone/attese annidate non introdurranno alcuna concorrenza. Recuperate tutti i tierOnes, quindi tutti i tierTwos per tierOne- # 1, quindi tutti i tierThrees per tierTwo- # 1 e così via, tutti eseguiti in sequenza (sebbene ci possa essere qualche concorrenza all'interno dei metodi Async di GetTier *).

Se si desiderano richieste simultanee, quindi TPL Dataflow è davvero una soluzione migliore.

+0

Non penso che sia così. Tutte le attività in un livello vengono avviate (correttamente) in modo indipendente (a causa dei metodi ** Select ** e "attesi" dai metodi ** WhenAll ** in seguito). La concorrenza non viene introdotta da async/await ma il fatto che LINQ viene utilizzato per creare le attività. L'unica cosa che posso pensare è che si esaurisce semplicemente i thread, succede nel caso in cui si abbiano a che fare con operazioni asincrone annidate (è quasi come una ricorsione che ovviamente si interrompe dopo pochi livelli - ma non prima di svuotare il pool di thread). –

+0

@ Miklós Mentre l'OP scrive, i suoi metodi 'GetAsync' sono veri I/O asincroni, quindi non bloccano alcun thread mentre sono in esecuzione. Ho eseguito 'GetData' (con' Task.Chiamate di ritardo) da un 'DispatcherSynchronizationContext', e non c'è concorrenza, quindi il pool di thread non può limitare le prestazioni. L'esecuzione di 'GetData' da un contesto di sincronizzazione nulla introduce la concorrenza, ma se l'I/O del server è più lento del codice di invio/ricezione (come descritto dall'OP), i colli di bottiglia nel pool di thread rappresentano una piccola percentuale del totale tempo di attesa. Il suo collo di bottiglia era probabilmente da qualche altra parte (come suggeriva Stephen Cleary). –