32

Ich bin verwirrt über den Unterschied zwischen dem Senden von Elementen über Post() oder SendAsync(). Mein Verständnis ist, dass in allen Fällen, sobald ein Element den Eingabepuffer eines Datenblocks erreicht hat, die Kontrolle an den aufrufenden Kontext zurückgegeben wird, richtig? Warum sollte ich dann SendAsync brauchen? Wenn meine Annahme falsch ist, frage ich mich im Gegensatz, warum jemand jemals Post() verwenden würde, wenn die ganze Idee der Verwendung von Datenblöcken darin besteht, eine gleichzeitige und asynchrone Umgebung einzurichten.TPL Dataflow, was ist der funktionale Unterschied zwischen Post() und SendAsync()?

Ich verstehe natürlich den Unterschied technisch darin, dass Post() einen bool zurückgibt, während SendAsync eine erwartete Aufgabe von bool zurückgibt. Aber welche Auswirkungen hat das? Wann würde die Rückgabe eines Bool (was ich verstehe, ist eine Bestätigung, ob der Gegenstand in die Warteschlange des Datenblocks gestellt wurde oder nicht) jemals verzögert werden? Ich verstehe die allgemeine Idee des Async/erwarten Parallelitätsframework, aber hier macht es nicht viel Sinn, denn anders als ein Bool wird das Ergebnis dessen, was mit dem übergebenen Element gemacht wird, nie an den Aufrufer zurückgegeben, sondern stattdessen in einem "out-queue" und entweder an verknüpfte Datenblöcke weitergeleitet oder verworfen.

Und gibt es irgendwelche Leistungsunterschiede zwischen den beiden Methoden beim Senden von Artikeln?

Antwort

39

Um den Unterschied zu sehen, brauchen Sie eine Situation, in der Blöcke ihre Nachrichten verschieben. In diesem Fall gibt Post sofort false zurück, während SendAsync eine Task zurückgibt, die abgeschlossen wird, wenn der Block entscheidet, was mit der Nachricht zu tun ist. Die Task wird ein true Ergebnis haben, wenn die Nachricht akzeptiert wird, und ein false Ergebnis wenn nicht.

Ein Beispiel für eine aufschiebende Situation ist eine nicht gierige Verbindung.Ein einfacheres Beispiel ist, wenn Sie BoundedCapacity gesetzt:

[TestMethod] 
public void Post_WhenNotFull_ReturnsTrue() 
{ 
    var block = new BufferBlock<int>(new DataflowBlockOptions {BoundedCapacity = 1}); 

    var result = block.Post(13); 

    Assert.IsTrue(result); 
} 

[TestMethod] 
public void Post_WhenFull_ReturnsFalse() 
{ 
    var block = new BufferBlock<int>(new DataflowBlockOptions { BoundedCapacity = 1 }); 
    block.Post(13); 

    var result = block.Post(13); 

    Assert.IsFalse(result); 
} 

[TestMethod] 
public void SendAsync_WhenNotFull_ReturnsCompleteTask() 
{ 
    // This is an implementation detail; technically, SendAsync could return a task that would complete "quickly" instead of already being completed. 
    var block = new BufferBlock<int>(new DataflowBlockOptions { BoundedCapacity = 1 }); 

    var result = block.SendAsync(13); 

    Assert.IsTrue(result.IsCompleted); 
} 

[TestMethod] 
public void SendAsync_WhenFull_ReturnsIncompleteTask() 
{ 
    var block = new BufferBlock<int>(new DataflowBlockOptions { BoundedCapacity = 1 }); 
    block.Post(13); 

    var result = block.SendAsync(13); 

    Assert.IsFalse(result.IsCompleted); 
} 

[TestMethod] 
public async Task SendAsync_BecomesNotFull_CompletesTaskWithTrueResult() 
{ 
    var block = new BufferBlock<int>(new DataflowBlockOptions { BoundedCapacity = 1 }); 
    block.Post(13); 
    var task = block.SendAsync(13); 

    block.Receive(); 

    var result = await task; 
    Assert.IsTrue(result); 
} 

[TestMethod] 
public async Task SendAsync_BecomesDecliningPermanently_CompletesTaskWithFalseResult() 
{ 
    var block = new BufferBlock<int>(new DataflowBlockOptions { BoundedCapacity = 1 }); 
    block.Post(13); 
    var task = block.SendAsync(13); 

    block.Complete(); 

    var result = await task; 
    Assert.IsFalse(result); 
} 
+0

ok, aber mit Ihrer Erklärung, was ist dann die Logik hinter "Task "? Wenn es sich aufgrund eines Aufschubs nicht sofort einreichen kann, aber die Aufgabe später abgeschlossen wird, ist der Unterschied zwischen dem Bool wahr und falsch? –

+1

Der Block kann schließlich entscheiden, diese Nachricht abzulehnen (z. B. wenn Sie den Block "abschließen"), in welchem ​​Fall das Ergebnis der Aufgabe "falsch" ist. Siehe aktualisierte Antwort. –

+0

Toll, das macht jetzt vollkommen Sinn, diese Möglichkeit ist mir völlig entfallen. Danke vielmals. –

7

Die Dokumentation macht dies einigermaßen klar, IMO. Insbesondere für Post:

Diese Methode wird zurückkehren, sobald der Zielblock den Punkt zu akzeptieren oder abzulehnen, aber wenn nicht anders diktiert durch spezielle Semantik des Zielblockes, es wartet nicht, bis das Produkt tatsächlich entschieden hat, wird verarbeitet.

Und:

Für Zielblocks, die dargebotener Nachrichten unterstützen zu verschieben, oder für Blöcke, die in ihrer Post Umsetzung mehr Verarbeitungs tun können, sollten SendAsync verwenden, die sofort zurück und wird das Ziel ermöglichen verschieben Sie die gepostete Nachricht und später konsumieren sie nach SendAsync zurück.

Mit anderen Worten, während beide die Nachricht in Bezug auf Verarbeitung asynchron sind, erlaubt SendAsync den Zielblock, ob oder nicht zu entscheiden die Nachricht asynchron akzeptieren.

Es klingt wie SendAsync ist eine allgemein "mehr asynchrone" Ansatz, und eine, die wahrscheinlich im Allgemeinen gefördert wird. Was ist nicht klar für mich ist, warum beide erforderlich sind, wie es sicherlich klingt wie Post ist weitgehend gleichwertig mit SendAsync und dann nur auf das Ergebnis warten.

+0

Dank, es machte es etwas klarer wenn Ihr letzter Satz fasst meine restlichen Verwirrung auf. Wenn ein Datenblock es ablehnt, eine Nachricht zu akzeptieren, habe ich nach meinen eigenen Tests keinen Vorteil bei der Verwendung von SendAsync über Post gesehen. Beide haben nicht versucht, die Nachricht erneut zuzustellen, wenn der Datenblock signalisiert, dass er zu einem späteren Zeitpunkt Nachrichten akzeptiert. (beide kehren sofort zurück, wenn die Nachricht abgelehnt wird und beide sofort zurückkehren, wenn die Nachricht akzeptiert wird). Die Semantik des "Akzeptierens" der Nachricht re Post gegenüber SendAsync ist mir immer noch nebulös. –

+0

Ich glaube, ich verstehe einfach nicht, wie viel Latenz möglicherweise in den "Annahme/Verweigerung" -Mechanismus von neuen übergebenen Nachrichten eingeführt werden könnte. Bisher habe ich noch keine messbaren Verzögerungen zwischen der Übergabe und Ankunft einer Nachricht in der Eingabewarteschlange/Ablehnung aus der Warteschlange gesehen. Aber danke, dass Sie den Fokus auf den Teil "Akzeptanz/Ablehnung" gelegt haben. –

+3

@Freddy: Sicher - aber der Unterschied ist, wenn ein Block * die Annahme/Ablehnungsentscheidung verschiebt *. Vielleicht macht der Zielblock, den Sie verwenden, das natürlich nicht. –