2015-06-04 15 views
5

Skalierung To Be habe ich folgendes (vereinfacht) Code:Nested Async/Await erscheint nicht

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()); 
} 

Basiert weg von dem, was ich gesehen habe, ist es nicht sehr gut Skalierung zu sein scheint. Alle Async Operationen sind "echte async" Operationen, was bedeutet, dass sie alle I/O sind.

Verwende ich Async/Await falsch für dieses Szenario? Ausgehend von meinen aktuellen Beobachtungen skaliere ich nicht so, wie ich es erwarten würde. Würde TPL DataFlow meine Lösung sein?

+0

"es scheint sehr gut skaliert zu sein" ist das ein Tippfehler und du solltest ein "nicht" da reinlegen? Und wenn ja, in welcher Art und Weise skalieren Sie, dass es schneller fertig wird oder das System nicht so stark belastet? Wie testen Sie die Skalierung? –

+2

Sie verwenden viele 'IEnumerables' als asynchrone Rückgabewerte. Sind Sie sicher, dass eine verzögerte Durchführung Ihre vermutete Parallelisierung nicht beeinträchtigt? – nvoigt

+0

@ScottChamberlain Ja das war ein Tippfehler. Ich erwarte, dass es schneller endet. Ich verstehe, dass es nur so schnell gehen wird, wie das Empfangsende meiner asynchronen Operationen ablaufen wird. Es sieht so aus, als würde ich, wenn ich 1500 Aufgaben spoole, mehr als nur 3 oder 4 im 'Warte'-Teil des Zustandsautomaten stecken bleiben. (Sorry, wenn das keinen Sinn ergibt. Es ist spät hier und ich bin sehr müde.) – Cameron

Antwort

0

Bei einem einzelnen Aufruf von GetData führen verschachtelte Async/await-Aufrufe nicht zu einem gemeinsamen Zugriff. Sie rufen alle TierOnes ab, dann alle TierTwos für TierOne - # 1, dann alle TierThrees für TierTwo - # 1 usw., die alle nacheinander ausgeführt werden (obwohl die GetTier * Async-Methoden möglicherweise eine gewisse Parallelität aufweisen).

Wenn Sie gleichzeitige Anfragen wünschen, dann ist TPL Dataflow in der Tat eine bessere Lösung.

+0

Ich glaube nicht, dass dies der Fall ist. Alle Aufgaben in einer Ebene werden (korrekt) unabhängig gestartet (wegen der Methoden ** Select ** und "warte" später durch die Methoden ** WhenAll **). Die Parallelität wird nicht durch async/await eingeführt, sondern durch die Tatsache, dass LINQ zum Erstellen der Tasks verwendet wird. Das einzige, was mir einfällt, ist, dass er einfach keine Threads mehr hat. Das passiert, wenn Sie verschachtelte Async-Operationen haben (es ist fast wie eine Rekursion, die nach einigen Ebenen aufhört - aber nicht bevor der Thread-Pool leer ist)). –

+0

@ Miklós Wie das OP schreibt, sind seine 'GetAsync'-Methoden echte asynchrone E/A, so dass sie während ihrer Ausführung keinen Thread blockieren. Ich habe 'GetData' (mit' Task."Delay" -Aufrufe) von einem 'DispatcherSynchronizationContext', und es gibt keine Nebenläufigkeit, sodass der Thread-Pool die Leistung nicht einschränken kann. Das Ausführen von 'GetData' aus einem NULL-Synchronisationskontext führt zwar zu Nebenläufigkeit, aber wenn die E/A zum Server langsamer ist als der Sende-/Empfangscode (wie das OP beschreibt), sind die Engpässe im Thread-Pool ein kleiner Prozentsatz der Gesamtsumme Wartezeit. Sein Flaschenhals war wahrscheinlich woanders (wie Stephen Cleary vorgeschlagen). –