Reactive Framework,PLINQ,TPL和Parallel Extensions如何相互关联?

时间:2010-01-26 09:08:08

标签: system.reactive plinq parallel-extensions task-parallel-library

至少从.NET 4.0发布以来,微软似乎已经付出了很多努力来支持并行和异步编程,而且似乎已经出现了很多API和库。特别是最近随处可见的以下花哨的名字:

  • Reactive Framework,
  • PLINQ(并行LINQ),
  • TPL(任务并行库)和
  • 并行扩展。

现在它们似乎都是微软的产品,它们似乎都针对.NET的异步或并行编程方案。但目前尚不清楚它们实际上是什么以及它们如何相互关联。有些可能实际上是相同的。

简而言之,任何人都可以直接记录什么是什么?

2 个答案:

答案 0 :(得分:94)

PLINQ (Parallel Linq)只是一种编写常规Linq查询的新方法,以便它们并行运行 - 换句话说,Framework将自动负责跨多个线程运行查询他们完成得更快(即使用多个CPU核心)。

例如,假设您有一堆字符串,并且您希望得到所有以字母“A”开头的字符串。您可以像这样编写查询:

var words = new[] { "Apple", "Banana", "Coconut", "Anvil" };
var myWords = words.Select(s => s.StartsWith("A"));

这很好用。但是,如果要搜索50,000个单词,则可能需要利用每个测试都是独立的事实,并将其拆分为多个核心:

var myWords = words.AsParallel().Select(s => s.StartsWith("A"));

这就是将常规查询转换为在多个核上运行的并行查询所需要做的全部工作。非常整洁。


TPL (任务并行库)是对PLINQ的补充,它们共同组成了Parallel Extensions。 PLINQ主要基于具有副作用的功能编程风格,而副作用恰恰是TPL的用途。如果您想要并行地做工作而不是仅仅并行搜索/选择事物,那么您可以使用TPL。

TPL本质上是Parallel类,它暴露ForForeachInvoke的重载。 Invoke有点像在ThreadPool中排队任务,但使用起来有点简单。 IMO,更有趣的位是ForForeach。例如,假设您有一大堆要压缩的文件。您可以编写常规顺序版本:

string[] fileNames = (...);
foreach (string fileName in fileNames)
{
    byte[] data = File.ReadAllBytes(fileName);
    byte[] compressedData = Compress(data);
    string outputFileName = Path.ChangeExtension(fileName, ".zip");
    File.WriteAllBytes(outputFileName, compressedData);
}

同样,此压缩的每次迭代完全独立于任何其他迭代。我们可以通过立即执行其中几个来加快速度:

Parallel.ForEach(fileNames, fileName =>
{
    byte[] data = File.ReadAllBytes(fileName);
    byte[] compressedData = Compress(data);
    string outputFileName = Path.ChangeExtension(fileName, ".zip");
    File.WriteAllBytes(outputFileName, compressedData);
});

而且,这就是并行化此操作所需的全部内容。现在,当我们运行CompressFiles方法(或我们决定调用它的任何方法)时,它将使用多个CPU内核,并且可能在一半或1/4时间内完成。

这个优点只是在ThreadPool中清除它,这实际上是同步运行。如果您使用ThreadPool代替(或只是普通的Thread实例),您必须想出一种方法来找出所有任务何时完成,而这不是< em>非常复杂,这是许多人往往搞砸或至少有麻烦的东西。当你使用Parallel课程时,你真的不必考虑它;多线程方面对你来说是隐藏的,它都是在幕后处理的。


反应性扩展(Rx)实际上是一个完全不同的野兽。这是一种考虑事件处理的不同方式。在这方面确实有很多材料需要讨论,但总而言之,不是将事件处理程序连接到事件,Rx允许您将事件序列视为......好,序列(IEnumerable<T>)。您可以以迭代方式处理事件,而不是在随机时间异步触发事件,您必须始终保持状态,以便检测以特定顺序发生的一系列事件。

我发现Rx最酷的一个例子是here。稍微跳转到“Linq to IObservable”部分,他只需4行代码即可实现拖放处理程序,这通常是WPF的难点。 Rx为您提供了组合事件,这些事情与常规事件处理程序并不相同,而且像这样的代码片段也很容易重构为可以在任何地方包含的行为类。


就是这样。这些是.NET 4.0中提供的一些较酷的功能。当然,还有几个,但这些是你问过的!

答案 1 :(得分:29)

我喜欢Aaronaught的答案,但我会说Rx和TPL解决了不同的问题。 TPL团队添加的部分内容是线程原语和运行时构建块的重要增强,如ThreadPool。您列出的所有内容都建立在这些原语和运行时功能之上。

但是TPL和Rx解决了两个不同的问题。当程序或算法“拉动”时,TPL效果最佳。排队”。当程序或算法需要对来自流的数据(如鼠标输入或从WCF等端点接收相关消息流)做出“反应”时,Rx表现优异。

您需要来自TPL的“工作单元”概念来完成像文件系统一样的工作,迭代集合,或像组织结构图一样走层次结构。在每种情况下,程序员都可以推断出工作总量,工作可以分解成一定大小的任务(任务),如果在层次结构上进行计算,任务可以“链接”在一起。因此,某些类型的工作适用于TPL的“任务层次结构”模型,并受益于对取消等管道的增强(请参阅关于CancellationTokenSource的第9频道视频)。 TPL还为诸如近实时数据处理等专业领域提供了大量旋钮。

Rx将是大多数开发人员应该最终使用的东西。 WPF应用程序可以如何“响应”外部数据(IM消息流到IM客户端)或外部输入(如从Aaronaught链接的鼠标拖动示例)。在底层,Rx使用来自TPL / BCL的线程原语,来自TPL / BCL的线程安全集合以及像ThreadPool这样的运行时对象。在我看来,Rx是表达你的意图的“最高级别”编程。

普通开发人员是否可以用你可以用Rx表达的意图来解决这个问题还有待观察。 :)

但我认为接下来的几年TPL与Rx将成为下一个争论,如LINQ-to-SQL vs. Entity Framework。在同一个域中有两种API,它们专门针对不同的场景,但在很多方面都有重叠。但在TPL&amp; amp; Rx实际上彼此了解并且有内置的适配器来组合应用程序并将两个框架一起使用(例如将PLINQ循环的结果输入到IObservable Rx流中)。对于那些没有进行任何并行编程的人来说,有很多学习可以加快速度。

更新:过去6个月(自我原来答案以来的18个月),我一直在常规工作中使用TPL和RxNet。我想在中间层WCF服务(企业LOB服务)中选择TPL和/或RxNet:http://yzorgsoft.blogspot.com/2011/09/middle-tier-tpl-andor-rxnet.html