TPL数据流和异步方法调用

时间:2012-09-06 13:23:43

标签: c# .net task-parallel-library async-await tpl-dataflow

我正在努力更好地理解并行处理的整个概念,并设置了测试用例。在使用测试之后,我发现在数据流ActionBlock(或TransformBlock)中使用异步方法调用不会对性能产生积极影响,它只会使代码复杂化。我是否正确地假设如果我使用Dataflow Blocks,其中的代码不必是异步的,Dataflow将使其本身异步。或者我错过了这一点?

2 个答案:

答案 0 :(得分:7)

TPL Dataflow不支持并发或并行化(尽管您可以使用ActionBlock来并行化其处理),这是并发或并行代码用于传递数据的东西。除此之外,它还是一种消息传递机制 - 它可以替代共享数据。共享数据由多个线程使用时需要昂贵的同步。正确完成后,消息传递不需要同步,因为需要处理的数据被封装在一条消息中,该消息被“发送”到将对其起作用的代码。

如果您有特定的设计,可以使用TPL Dataflow。如果您没有专门实现Actor-based编程,message-passing或非阻塞生产者/消费者方案等内容,那么TPL Dataflow可能会使事情变得复杂。

如果您认为您可能想要设计这样的系统,那么就像Stephen Toub(Microsoft的并行团队成员)所理解的TPL Dataflow(TDF)一样有很好的资源,如video以及Dataflow MSDN page

更新

您可以设置块的最大并行度,但将其设置为高于CPU或内核的数量通常会适得其反。假设每个执行的操作都是或多或少的CPU限制(在运行时使用100%的CPU)。如果这些操作花了很多时间等待(等待等待句柄,等待消息泵中的消息 - 这对于一个动作来说是不正常的,而是用于UI威胁等等)那么一定程度的并行性超出了CPU的数量可能有意义(虽然这很难调整)。当运行的操作多于 CPU绑定的CPU时,您真正开始对操作系统进行任务。操作系统想要为每个线程(或本例中的每个操作)放弃CPU时间,因为它正在“运行”。当没有足够的CPU可用时,操作系统开始抢占式多任务,循环为每个活动线程提供CPU时间。每次操作系统将CPU从一个线程中取出并将其交给另一个线程时,称为context switching。这真的昂贵(在2000-8000 cpu cycles范围内)。因此,操作系统实际上是花费所有时间进行上下文切换而不是运行您的操作。

如果您的操作确实是异步的,那么块的并行度是无关紧要的,因为其他东西正在进行并行化。但是,同样的问题出现了,您的异步操作正在被执行而未经检查,您可能会在您引入的上下文切换程度上压倒操作系统。由于缺乏控制,我会认真考虑进行异步操作。

答案 1 :(得分:1)

TPL Dataflow不适合所有类型的并行处理,也不会让您的代码神奇地更快。

TDF背后的主要思想是你有自己独立完成工作的积木。这意味着每个块的工作可以在一个单独的线程上执行,因此在某些情况下使用TDF并行化代码非常简单。

如果块内的代码使用某些无法共享的资源,则此功能尤其有用。这样,您可以充分利用该共享资源,因为此块的处理独立于其他块。

一般来说,如果你的代码就像一个管道,TDF最合适:一个项目进入,由阶段1处理,然后由阶段2处理,......,最后它到达输出。虽然数据流网络可能比这复杂得多。但你不应该试图强迫这一点,如果你想要做的事情与TDF不相符,那么你只会使你的代码复杂化而没有任何好处。