system.reactive - Reactive Framework、PLINQ、TPL 和并行扩展如何相互关联?

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

至少自 .NET 4.0 发布以来,微软似乎在支持并行和异步编程方面投入了大量精力,并且似乎出现了很多围绕此的 API 和库。尤其是下面这些花哨的名字最近到处都被不断提及:

  • 响应式(Reactive)框架,
  • PLINQ(并行 LINQ),
  • TPL(任务并行库)和
  • 并行扩展。

现在它们似乎都是 Microsoft 产品,并且它们似乎都针对 .NET 的异步或并行编程场景。但目前尚不清楚它们各自的实际含义以及它们之间的关系。有些实际上可能是同一件事。

用几句话来说,谁能澄清什么是什么?

最佳答案

PLINQ(并行 Linq)只是一种编写常规 Linq 查询的新方法,以便它们并行运行 - 换句话说,框架将自动负责跨多个线程运行查询,以便他们完成得更快(即使用多个 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 的补充,它们共同构成了并行扩展。尽管 PLINQ 主要基于函数式编程风格,副作用,但副作用正是 TPL 的用途。如果您想实际上并行工作,而不是仅仅并行搜索/选择内容,则可以使用 TPL。

TPL 本质上是 Parallel公开 For 重载的类, Foreach ,和InvokeInvoke有点像在 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 实例),您必须想出一种方法来找出所有任务何时完成,虽然这并不非常复杂,但它是一些东西很多人往往会搞砸或者至少遇到麻烦。当您使用Parallel时类,你实际上不必考虑它;多线程方面对您来说是隐藏的,一切都在幕后处理。

<小时/>

响应式(Reactive)扩展 (Rx) 确实是一种完全不同的野兽。这是考虑事件处理的不同方式。关于这一点确实有很多内容需要介绍,但长话短说,Rx 可以让您将事件序列视为……序列( IEnumerable<T> ),而不是将事件处理程序连接到事件。您可以以迭代方式处理事件,而不是在随机时间异步触发事件,在这种方式中,您必须始终保存状态才能检测以特定顺序发生的一系列事件。

我发现的最酷的 Rx 示例之一是 here 。跳到“Linq to IObservable”部分,他仅用 4 行代码就实现了一个拖放处理程序,这在 WPF 中通常是一个难题。 Rx 为您提供了事件的组合,这是常规事件处理程序所不具备的,并且像这样的代码片段也可以直接重构为可以嵌入到任何地方的行为类。

<小时/>

就是这样。这些是 .NET 4.0 中提供的一些更酷的功能。当然还有更多,但这些是您询问的!

关于system.reactive - Reactive Framework、PLINQ、TPL 和并行扩展如何相互关联?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2138361/

相关文章:

c# - 使用 Console 和 PLinq 的明显死锁

c# - PLINQ ForAll WithCancellation 不起作用

c# - 为什么组合 Task.Run 和 plinq 这么慢?

.net - c# 4.0 Parallel.For 需要任何特殊引用吗?

c# - 在使用 Rx 的最后一个值的指定增量之后,如何让位置更新通过?

c# - 在 c# 中使用 Rx Reactive 扩展的 linqpad 错误

.net - "Push"linq 与响应式(Reactive)框架

c# - 可观察到 Rx 中的回调

.net - Parallel.为对象列表调用相同的方法

.net - BlockingCollection(Of T)的目的是什么