I am confused about the difference between sending items through Post() or SendAsync(). My understanding is that in all cases once an item reached the input buffer of a data block, control is returned to the calling context, correct? Then why would I ever need SendAsync? If my assumption is incorrect then I wonder, on the contrary, why anyone would ever use Post() if the whole idea of using data blocks is to establish a concurrent and async environment.
I understand of course the difference technically in that Post() returns a bool whereas SendAsync returns an awaitable Task of bool. But what implications does that have? When would the return of a bool (which I understand is a confirmation whether the item was placed in the queue of the data block or not) ever be delayed? I understand the general idea of the async/await concurrency framework but here it does not make a whole lot sense because other than a bool the results of whatever is done to the passed-in item is never returned to the caller but instead placed in an "out-queue" and either forwarded to linked data blocks or discarded.
And is there any performance difference between the two methods when sending items?
To see the difference, you need a situation where blocks will postpone their messages. In this case, Post
will return false
immediately, whereas SendAsync
will return a Task
that will be completed when the block decides what to do with the message. The Task
will have a true
result if the message is accepted, and a false
result if not.
One example of a postponing situation is a non-greedy join. A simpler example is when you set BoundedCapacity
:
[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); }
The documentation makes this reasonably clear, IMO. In particular, for Post
:
This method will return once the target block has decided to accept or decline the item, but unless otherwise dictated by special semantics of the target block, it does not wait for the item to actually be processed.
And:
For target blocks that support postponing offered messages, or for blocks that may do more processing in their
Post
implementation, consider usingSendAsync
, which will return immediately and will enable the target to postpone the posted message and later consume it afterSendAsync
returns.
In other words, while both are asynchronous with respect to processing the message, SendAsync
allows the target block to decide whether or not to accept the message asynchronously too.
It sounds like SendAsync
is a generally "more asynchronous" approach, and one which is probably encouraged in general. What isn't clear to me is why both are required, as it certainly sounds like Post
is broadly equivalent to using SendAsync
and then just waiting on the result. As noted in comments, there is one significant difference: if the buffer is full, Post
will immediately reject, whereas SendAsync
doesn't.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With