Reactive Framework,PLINQ,TPL和并行扩展如何相互关联?

至less从.NET 4.0发布以来,微软似乎已经付出了很多的努力来支持并行和asynchronous编程,并且围绕这一点出现了许多API和库似乎。 特别是最近到处都有人提到下面这些奇特的名字:

  • react native框架,
  • PLINQ(并行LINQ),
  • TPL(任务并行库)和
  • 并行扩展。

现在,他们似乎都是微软的产品,他们似乎都针对.NET的asynchronous或并行编程scheme。 但是,他们究竟是什么以及他们是如何相互关联的还不太清楚。 有些可能实际上是相同的东西。

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

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

例如,假设你有一串string,并且想要得到所有以字母“A”开始的string。 你可以这样写你的查询:

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

这工作正常。 如果你有5万个词的search,你可能想要利用每个testing都是独立的事实,并将其分成多个核心:

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

只需要将常规查询转换为在多核上运行的并行查询即可。 很简约。


TPL (任务并行库)是对PLINQ的补充,它们一起组成并行扩展。 PLINQ在很大程度上是基于function性的编程风格, 没有任何副作用,而副作用正是TPL的目的。 如果你想实际上并行工作 ,而不是仅仅并行地search/select东西,那么你可以使用TPL。

TPL本质上是公开了ForForeachInvoke重载的Parallel类。 Invoke有点像排队在ThreadPool任务,但使用起来有点简单。 国际海事组织,更有趣的是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内核,并可能完成一半或四分之一的时间。

这只是在ThreadPool全部夹紧的优点是,这实际上是同步运行的 。 如果你使用了ThreadPool (或者只是简单的Thread实例),你将不得不想出一个办法来确定什么时候所有的任务都完成了,虽然这不是非常复杂,但是这是很多人们往往搞砸了,或者至less有麻烦。 当你使用Parallel类的时候,你并不需要考虑它。 multithreading方面是隐藏的,都是在幕后处理的。


反应式扩展 (Rx)实际上是一个完全不同的野兽。 这是对事件处理的不同思考方式。 这里有很多材料可以覆盖,但是为了简短起见,Rx不是将事件处理程序连接到事件,而是让事件序列作为…序列( IEnumerable<T> )来处理。 您可以以迭代的方式处理事件,而不是随机地随意触发事件,必须始终保存状态以检测以特定顺序发生的一系列事件。

我发现的最酷的例子之一是在这里 。 跳到“Linq to IObservable”部分,在那里他用4行代码实现了拖放处理程序,这在WPF中通常很痛苦。 Rx给了你事件的组合 ,而事实上你并不需要使用常规的事件处理程序,像这样的代码片断也可以直接重构成行为类,你可以将它放在任何地方。


就是这样。 这些是在.NET 4.0中可用的一些较酷的function。 当然还有更多,但是这些是你问的!

我喜欢Aaronaught的回答,但我会说Rx和TPL解决不同的问题。 TPL团队添加的部分内容是ThreadPool的线程原语和对运行时构build块的显着增强。 而且您列出的所有内容都build立在这些原语和运行时function之上。

但是TPL和Rx解决了两个不同的问题。 当程序或algorithm是“拉和排队”时,TPL效果最好。 当程序或algorithm需要对stream中的数据作出“反应”时(例如鼠标input或从类似WCF的端点接收到相关消息stream时),Rx会更胜一筹。

您需要TPL的“工作单元”概念来完成像文件系统一样的工作,遍历一个集合,或像组织图表一样走一个层次结构。 在每一种情况下,程序员都可以推理工作总量,工作可以分解成一定规模的大块(任务),并且在计算层次结构时,任务可以“链接”在一起。 所以某些types的工作适合于TPL的“任务层次”模型,并从像取消这样的pipe道增强中受益(请参阅第9频道的CancellationTokenSourcevideo)。 TPL还为专业领域提供了很多旋钮,如近实时数据处理。

Rx将是大多数开发者最终使用的。 这就是WPF应用程序如何能够像外部数据(IM消息stream到IM客户端)或外部input(如从Aaronaught链接的鼠标拖动示例)那样对外部消息做出反应。 在封面下,Rx使用来自TPL / BCL的线程原语,来自TPL / BCL的线程安全集合以及ThreadPool等运行时对象。 在我看来,Rx是编程的“最高级别”来expression你的意图。

一般的开发人员是否可以用Rx来expression他们的意图,但是还没有被看到。 🙂

但是我认为,未来几年TPL vs. Rx将成为下一个像LINQ-to-SQL vs. Entity Framework这样的辩论。 在同一个域中有两种风格的API,专门用于不同的场景,但是有许多方面的重叠。 但是在TPL&Rx的情况下,它们实际上是相互了解的,并且有内置的适配器来组合应用程序并将两个框架一起使用(如将PLINQ循环的结果送入IObservable Rxstream)。 对于没有进行任何并行编程的人来说,有很多的学习要加快速度。

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