TPL Dataflow,Post() 和 SendAsync() 之间的功能区别是啥?
Posted
技术标签:
【中文标题】TPL Dataflow,Post() 和 SendAsync() 之间的功能区别是啥?【英文标题】:TPL Dataflow, whats the functional difference between Post() and SendAsync()?TPL Dataflow,Post() 和 SendAsync() 之间的功能区别是什么? 【发布时间】:2012-11-15 23:07:43 【问题描述】:我对通过 Post() 或 SendAsync() 发送项目的区别感到困惑。我的理解是,在所有情况下,一旦项目到达数据块的输入缓冲区,控制就会返回到调用上下文,对吗?那我为什么需要 SendAsync 呢?如果我的假设不正确,那么我想知道,相反,如果使用数据块的整个想法是建立一个并发和异步环境,为什么有人会使用 Post()。
我当然理解 Post() 返回一个 bool 而 SendAsync 返回一个可等待的 bool 任务的技术差异。但这有什么影响?什么时候会延迟返回布尔值(我的理解是确认该项目是否已放入数据块的队列中)?我了解 async/await 并发框架的一般概念,但在这里它没有多大意义,因为除了 bool 之外,对传入项目所做的任何事情的结果都不会返回给调用者,而是放在一个“out-queue”并转发到链接的数据块或丢弃。
在发送项目时,这两种方法之间有什么性能差异吗?
【问题讨论】:
来自this blog 的相关引用:1) 动作块的客户端可以提供队列大小(在构造函数中)。 2) 当队列已满时,Post 方法返回 false,SendAsync 方法“阻塞”,直到队列获得空闲位置。 【参考方案1】:要查看差异,您需要一个块将延迟其消息的情况。在这种情况下,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);
【讨论】:
区块可能最终决定拒绝该消息(例如,如果您Complete
该区块),在这种情况下,任务的结果将是 false
。查看更新的答案。
是的,是的,但不完全是。如果块推迟消息,SendAsync
将构造一个“消息持有者”,它将保存消息,直到块接收或拒绝它。 Task
实际上是消息持有者的一部分。此外,在处理延迟时,目标块有一个数据结构来跟踪其延迟的消息源(包括“消息持有者”)。
Post
立即返回;所以如果区块推迟,它将返回false
。 Post
没有“消息持有者”。有一个很棒的文档 Guide to Implementing Custom TPL Dataflow Blocks 更深入地介绍了 Dataflow 块的实际工作原理。
正确。因此,您需要检查 Post
是否返回 false
,如果您不想丢弃它,请对消息执行其他操作。
这是否意味着每个人都应该始终使用 SendAsync,除非他们可以接受丢弃消息?【参考方案2】:
IMO,文档说明了这一点。特别是对于Post
:
一旦目标块决定接受或拒绝该项目,此方法将返回,但除非目标块的特殊语义另有规定,否则它不会等待该项目被实际处理。
还有:
对于支持延迟提供消息的目标块,或者对于可能在其
Post
实现中进行更多处理的块,考虑使用SendAsync
,它将立即返回并使目标能够延迟发布消息并稍后消费在SendAsync
返回之后。
换句话说,虽然两者对于处理消息都是异步的,但SendAsync
允许目标块决定是否也异步接受消息.
听起来SendAsync
是一种通常“更异步”的方法,并且通常可能受到鼓励。我不清楚的是为什么两者都是必需的,因为听起来Post
大致相当于使用SendAsync
,然后只是等待结果。正如 cmets 中所述,有一个显着的区别:如果缓冲区已满,Post
将立即拒绝,而 SendAsync
不会。
【讨论】:
谢谢,虽然你的最后一句话总结了我剩下的困惑,但它使它更清楚了。根据我自己的测试,当数据块拒绝接受消息时,我没有看到使用 SendAsync 优于 Post 的任何优势,当数据块发出信号表示它稍后会接受消息时,两者都没有尝试重新传递消息。 (如果消息被拒绝,两者都立即返回,如果消息被接受,两者都立即返回)。因为“接受”消息 re Post vs SendAsync 的语义对我来说仍然是模糊的。 我想我只是不明白在新传递消息的“接受/拒绝”机制中可能会引入多少延迟。到目前为止,我从未见过输入队列中消息的传递和到达/来自队列的拒绝之间的任何可测量的延迟。但无论如何,感谢您将重点放在问题的“接受/拒绝”部分。 @Freddy:当然 - 但不同之处在于,当一个区块推迟接受/拒绝决定时。当然,也许您使用的目标块永远不会这样做。 ❝Post 大致相当于使用 SendAsync 然后等待结果。❞ 我认为这是不正确的。如果输入缓冲区已满,Post(x)
不会等待,而SendAsync(x).Wait()
会等待。
@TheodorZoulias:将编辑以突出这种差异。我确实说“广泛地”:)以上是关于TPL Dataflow,Post() 和 SendAsync() 之间的功能区别是啥?的主要内容,如果未能解决你的问题,请参考以下文章
使用 TPL-Dataflow 进行聚合和连接(内、外、左……)?
TPL Dataflow LinkTo TransformBlock 非常慢