充分利用.NET任务并行库

问题1。

使用Parallel.For和Parallel.ForEach更适合处理有序或无序的任务吗?

我问的原因是我最近更新了一个串行循环,其中一个StringBuilder用于根据各种参数生成一个SQL语句。 结果是,与使用标准foreach循环相比,SQL有点混乱(到了包含语法错误的程度),因此我的直觉是TPL不适合执行数据必须出现在特别的顺序。

问题2。

TPL是否自动使用多核架构,我必须在执行前配置任何内容吗?

我提出这个问题的原因与我提出的有关TPL操作性能分析的问题有关。 对这个问题的回答使我明白TPL并不总是比标准串行循环更有效,因为应用程序可能无法访问多个内核,因此创建额外线程和循环的开销会导致性能下降到标准的串行循环。

我的直觉是,TPL不适合执行数据必须以特定顺序出现的任务。

正确。 如果你期望事情有序,你可能会误解你在“并行化”一个循环时会发生什么。

TPL是否自动使用多核架构,我必须在执行前配置任何内容吗?

请参阅msdn杂志上的以下文章: http : //msdn.microsoft.com/en-us/magazine/cc163340.aspx

使用该库,您可以方便地在现有顺序代码中表达潜在的并行性,其中公开的并行任务将在所有可用处理器上并发运行。

  1. 如果必须对结果进行排序,那么为了使循环并行化,您需要能够以任何顺序进行实际工作,然后对结果进行排序。 根据情况,这可能比首先连续进行工作更有效,也可能没有效率。 如果以任何顺序进行并行化工作的好处超过了对结果进行排序的成本,那么它就是净增益。 如果该任务不够复杂,您的硬件不允许大量并行化,或者如果它没有很好地并行化(即由于数据依赖性而导致大量等待),那么对结果进行排序可能需要更多比并行化循环所获得的时间(或者更糟糕的是,即使没有排序,并行循环也需要更长时间,请参阅问题二),因此您不应该并行化它。

    请注意,如果实际工作单元需要按特定顺序运行,而不是仅按特定顺序运行结果,那么您将无法对其进行并行化,或者您将无法对其进行并行化几乎同样有效。 如果您没有正确地同步访问共享资源,那么您实际上会得到错误的结果(如您的情况中所发生的那样)。 为此,您需要记住,如果您实际上无法获得正确的结果,那么性能优化就毫无意义。

  2. 你真的不需要担心TPL的硬件问题。 您无需显式添加或限制任务。 虽然有几种方法可以做到,但几乎任何时候你做这样的事情都会损害性能。 当你做这样的事情时,你正在为TPL 添加限制,所以它无法做到它想要的。 通常它比你更清楚。

    你也可以在这里触及另一个点,那就是并行循环通常需要更长的时间(你只是没有给出导致这种行为的可能原因)。 通常,需要完成的实际工作非常小,以至于创建线程,管理它们,处理上下文移动和根据需要同步数据的工作可能比通过并行工作获得的工作更多。 这就是为什么在决定并行化某些工作以确保它实际上从中受益时实际进行大量测试很重要的原因。

  1. 对于无序列表而言,这并不是更好更糟 – 您在#1中的问题是您对StringBuilder有共享依赖,这就是并行查询失败的原因。 TPL在独立工作单位上表现出色。 即使这样,也可以使用简单的技巧强制评估并行查询,并在并行操作完成后保持结果的原始顺序。

  2. TPL和PLINQ在技术上是截然不同的; PLINQ使用TPL来实现它的目标。 也就是说,PLINQ尝试检查您的体系结构并尽可能地构建集合的执行。 TPL只是围绕任务架构的包装器。 由您来决定创建任务(类似于1MB内存)的开销,以及执行任务的上下文切换的开销是否大于简单地串行运行任务。

在第1点,如果使用TPL,您不知道运行哪个任务的顺序。 这就是并行与顺序之美。 有办法控制事物的顺序但是你可能会失去并行的好处。

在2:TPL开箱即用多核。 但是使用多个线程确实总是有开销。 调度程序上的负载增加,线程(上下文)切换不是免费的。 为了保持数据同步以避免竞争条件,您可能需要一些锁定机制,这也会增加开销。

使用TPL制作快速并行算法变得更加容易,但仍然是某种艺术。

显然,TPL不是构建像查询这样的有序集的好工具。

如果要对一组项目执行一系列任务,则可以使用BlockingCollection。 任务可以并行执行,但保持该组的顺序。

BlockingCollection类