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

bit*_*onk 66 parallel-extensions plinq task-parallel-library system.reactive

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

  • 反应性框架,
  • PLINQ(并行LINQ),
  • TPL(任务并行库)和
  • 并行扩展.

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

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

Aar*_*ght 95

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

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

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

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

var myWords = words.AsParallel().Select(s => s.StartsWith("A"));
Run Code Online (Sandbox Code Playgroud)

这就是将常规查询转换为在多个核上运行的并行查询所需要做的全部工作.很简约.


TPL(任务并行库)是排序的补充PLINQ的,和他们一起构成了并行扩展.PLINQ主要基于功能风格的编程而没有副作用,而副作用正是TPL的用途.如果你想实际并行工作而不是仅仅并行搜索/选择事物,你可以使用TPL.

第三方物流基本上是Parallel暴露的重载类For,ForeachInvoke. 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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

这样做的好处ThreadPool就在于它实际上是同步运行的.如果你使用了ThreadPool(或者只是简单的Thread实例),你必须找到一种方法来找出所有任务何时完成,虽然这并不是非常复杂,但很多人都倾向于搞砸或至少有麻烦.当你使用这门Parallel课时,你真的不必考虑它; 多线程方面对你来说是隐藏的,它都是在幕后处理的.


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

我发现Rx最酷的例子之一就在这里.跳到"Linq to IObservable"部分,他只用4行代码实现了一个拖放处理程序,这通常是WPF中的一个难点.Rx为您提供了事件的组合,这些事件与常规事件处理程序并不存在,并且像这样的代码片段也可以直接重构为可以在任何地方包含的行为类.


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

  • 与C#异步CTP一起发布的TPL数据流库(TDF)看起来非常好.它应该使.NET中间件更容易保持在并行处理的"性能最佳点".要获得最佳性能(考虑吞吐量和延迟),您需要设置限制.因此,处理的每个"块"都可以拥有自己的"缓冲区上限",并且可以具有并行上限.您还可以告诉每个块处理批次而不是单个消息.这看起来很棒. (2认同)

yzo*_*org 30

我喜欢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和Rx的情况下,它们实际上是相互了解的,并且有内置的适配器来组合应用程序并将两个框架一起使用(例如将PLINQ循环的结果提供给IObservable Rx流).对于那些没有进行任何并行编程的人来说,有很多学习可以加快速度.

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