TPL Dataflow和异步方法调用

3

我正在尝试更好地理解并行处理的整个概念,并设置测试用例。在尝试了这些测试之后,我发现在Dataflow中使用异步方法调用ActionBlock(或TransformBlock)并不会对性能产生积极影响,反而会使代码更加复杂。我的理解是,如果我正在使用Dataflow块,则其中的代码不必是异步的,Dataflow将自行使其异步化。或者我是错过了什么重要的点吗?

2个回答

8
TPL Dataflow不支持并发或并行(尽管您可以像使用ActionBlock来并行处理它的处理),它是并发或并行代码用于通信数据的一种机制。除此之外,它还是一种消息传递机制 - 这在某种程度上是共享数据的替代方法。当多个线程使用共享数据时,需要昂贵的同步。当正确执行消息传递时,不需要同步,因为需要处理的数据封装在“发送”给将处理它的代码的消息中。
如果您有特定的设计,则可以使用TPL Dataflow。如果您没有专门实现类似于基于Actor的编程,或消息传递,或非阻塞生产者/消费者场景,则TPL Dataflow可能会使事情复杂化。
如果您认为可能想要设计这样的系统,那么有一些很好的资源可以帮助您理解TPL Dataflow(TDF),例如Microsoft并行团队成员Stephen Toub的视频以及Dataflow MSDN页面。请注意,保留HTML标签。
你可以为一个块设置最大并行度,但将其设置得高于 CPU 或核心数量通常是适得其反的。假设每个执行的操作都是更多或少地绑定在 CPU 上(在运行时使用 100% 的 CPU)。如果操作花费了很多时间等待(等待等待句柄,在消息泵中等待消息 - 这对于操作而言不正常,但对于 UI 线程而言是正常的等待),那么超过 CPU 数量的并行度可能是有意义的(尽管这很难调整)。当你拥有比 CPU 数量更多的操作正在运行且这些操作是 CPU 绑定的时候,你真的开始任务 OS。OS 希望为每个线程(或在本例中的每个操作)放弃 CPU 时间,因为它正在 "运行"。当没有足够的 CPU 可以使用时,OS 开始进行抢占式多任务处理,轮流为每个活动线程分配 CPU 时间。每次 OS 从一个线程取走 CPU 并将其给另一个线程被称为上下文切换。这是非常昂贵的(在 2000-8000 CPU 循环 范围内)。因此,OS 实际上花费所有时间进行上下文切换,而不是运行您的操作。
如果您的操作确实是异步的,那么块的并行度是无关紧要的,因为其他东西正在进行并行处理。但是,同样的问题出现了,您的异步操作正在未经检查地执行,并且存在过多的上下文切换风险,可能会使操作系统不堪重负。考虑到这种缺乏控制,我会认真考虑不要执行异步操作。

你可以使用ActionBlock设置并行级别。我相信默认情况下不会并行执行(一个块一个接一个地执行)。请参阅http://msdn.microsoft.com/en-us/library/hh228609.aspx以获取更多详细信息。 - Peter Ritchie
@Dimitri 我的回复比评论要长得多,所以我已经更新了答案。 - Peter Ritchie
@svick 你可能会想到TPL和ThreadPool调度程序。ThreadPool默认为每个CPU/核心25个线程,这已经足以引起上下文切换问题。(不是TPL的ThreadPool.QueueUserWorkItem...)但是,如果您告诉TPL您希望具有最大并行度,则意味着您要求每个任务都拥有自己的线程(好吧,您要求所有任务同时运行 - 目前唯一的方法就是为每个任务分配一个线程)。 - Peter Ritchie
@svick 实际上,这在这里有记录:http://msdn.microsoft.com/zh-cn/library/ff963552.aspx - Peter Ritchie
1
@PeterRitchie 而将 MaxDegreeOfParallelism 设置为无限制并不意味着每个项目都将在自己的线程上处理,那确实非常低效。它的意思是它将使用线程池允许的尽可能多的线程,这通常会是每个核心大约1个线程。 - svick
显示剩余8条评论

1

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

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

如果块内部的代码使用了一些无法共享的资源,这种方法尤其有用。这样,您可以充分利用该共享资源,因为该块的处理与其他块无关。

总的来说,如果您的代码像一个管道:一个项目进入,经过第一阶段处理,然后经过第二阶段处理,最终到达输出,那么 TDF 最适合您。虽然数据流网络可能比这更复杂。但如果您想做的事情与 TDF 不太匹配,您就不应该强制使用它,否则只会使您的代码变得更加复杂而没有任何好处。


感谢svick的建议。我的情况是一个完美的数据管道示例,在这个示例中,原始数据经过不同阶段的处理,最终被持久化到数据库中。 - Dimitri

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