TPL Dataflow,Post()和SendAsync()之间的功能区别是什么?

64
我对通过Post()或SendAsync()发送项目的区别感到困惑。我的理解是,无论何时,一旦一个项目达到数据块的输入缓冲区,控制权就会返回给调用上下文,对吗?那么我为什么需要SendAsync呢?如果我的假设不正确,那么相反的是,如果使用数据块的整个思路是建立并发和异步环境,那么为什么有人要使用Post()呢?
当然,从技术上讲,我理解Post()返回一个布尔值,而SendAsync返回一个可等待的bool任务。但这意味着什么呢?什么情况下布尔值(我理解的是确认项目是否被放置在数据块的队列中)会延迟呢?我理解async/await并发框架的概念,但这里它并没有太多意义,因为除了布尔值之外,传入项目所做的任何结果都不会返回给调用者,而是放置在“out-queue”中,然后转发到链接的数据块或丢弃。
在发送项目时,这两种方法之间是否有任何性能差异呢?

来自这篇博客的相关引用:1)一个操作块的客户端可以在构造函数中提供一个队列大小。2)当队列已满时,Post方法返回false,而SendAsync方法则“阻塞”,直到队列有一个空位为止。 - noseratio - open to work
2个回答

73
为了看到区别,您需要一个情况,其中块将推迟它们的消息。在这种情况下,Post会立即返回false,而SendAsync会返回一个Task,该任务将在块决定如何处理消息时完成。如果接受了消息,则Task将具有true结果,如果没有,则具有false结果。
一个推迟的情况的例子是非贪婪连接。当您设置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);
}

1
该块可能最终决定拒绝该消息(例如,如果您“完成”该块),在这种情况下,任务的结果将为“false”。请参见更新的答案。 - Stephen Cleary
3
是的,是的,还有一点不太对。如果阻塞了消息,SendAsync 将构造一个"消息持有者",直到该块接收或拒绝该消息为止。Task 实际上是消息持有者的一部分。此外,在处理延迟时,目标块具有数据结构来跟踪其延迟的消息来源(包括"消息持有者")。 - Stephen Cleary
3
如果执行块需要延迟,则Post会立即返回,并返回falsePost没有“消息保持器”。有一份很棒的文档[实现自定义TPL数据流块指南](http://download.microsoft.com/download/1/6/1/1615555D-287C-4159-8491-8E5644C43CBA/Guide%20to%20Implementing%20Custom%20TPL%20Dataflow%20Blocks.pdf),详细介绍了数据流块的工作原理。 - Stephen Cleary
1
正确。因此,如果您不想放弃消息,则需要检查Post是否返回false,并对其执行其他操作。 - Stephen Cleary
6
这是否意味着每个人都应该始终使用SendAsync,除非他们可以接受消息丢失? - Shital Shah
显示剩余10条评论

22
文档在这方面已经相当清楚,特别是对于Post方法:

该方法一旦目标块决定接受或拒绝该项就会返回,但除非目标块的特殊语义另有规定,否则它不会等待项目实际被处理。

而且:

针对支持推迟提供的消息的目标块,或者针对可能在其Post实现中执行更多处理的块,请考虑使用SendAsync,它将立即返回,并将使目标能够推迟发布的邮件并在SendAsync返回后稍后消费它。

换句话说,虽然两者都是关于处理消息的异步操作,但SendAsync允许目标块异步决定是否接受该消息。

听起来SendAsync是一种通常更“异步”的方法,可能是鼓励的方法。我不明白为什么两者都是必需的,因为它确实听起来像Post与使用SendAsync,然后只需等待结果基本等效。正如在评论中指出的那样,有一个显着的区别:如果缓冲区已满,则Post将立即拒绝,而SendAsync则不会。


1
谢谢,尽管你的最后一句话概括了我剩下的困惑,但它使得问题更加清晰了。从我的测试来看,当数据块拒绝接受消息时,我没有看到使用SendAsync比Post有任何优势,两者都不会在数据块在稍后的时间点信号接受消息时重新传递消息(如果消息被拒绝,则立即返回,如果消息被接受,则立即返回)。在这方面,“接受”消息的语义对于Post和SendAsync仍然是模糊的。 - Matt
我想我不太明白在新传递的消息的“接受/拒绝”机制中可能会引入多少潜在延迟。到目前为止,我从未看到过在输入队列中传递和到达消息/从队列中拒绝之间有任何可测量的延迟。但无论如何,还是感谢您将焦点放在了问题的“接受/拒绝”部分。 - Matt
4
@Freddy:当一个区块延迟接受/拒绝的决定时,它与其他区块不同。当然,你使用的目标区块可能从不这样做。 - Jon Skeet
1
“Post”在很大程度上等同于使用“SendAsync”,然后只需等待结果。我认为这不正确。如果输入缓冲区已满,“Post(x)”不会等待,而“SendAsync(x)。Wait()”会等待。 - Theodor Zoulias
@TheodorZoulias:会进行编辑以突出这种差异。我确实说过“广泛地” :) - Jon Skeet
谢谢!即使是 Stephen Cleary 在他的博客中也有错误。 :-) (❝一旦达到限流阈值,帖子将(同步)阻塞❞) - Theodor Zoulias

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接