线程管理建议 - TPL是一个好的选择吗?

5
我希望能得到有关线程管理和任务并行库的建议,因为我不确定自己一直在正确的路上。简单来说,我的目标是使用基于启发式算法生成一个“解决方案”,以下是我的大致思路:
首先需要计算出一个基本解决方案,这个操作可能无法并行化,所以我们不需要担心。
一旦初始解决方案被生成,我想要触发n个线程,尝试找到更好的解决方案。这些线程需要完成以下几个任务:
1.它们需要使用不同的“优化指标”进行初始化。换句话说,它们试图通过代码中设置的优先级来优化不同的事物。这意味着它们都运行着稍微不同的计算引擎。我不确定是否可以使用TPL实现这一点。
2.如果其中一个线程找到了比当前已知最佳解决方案更好的解决方案(需要在所有线程之间共享),则需要更新最佳解决方案,并强制重新启动一些其他线程(这取决于优化指标的优先级)。
3.我可能还希望将某些计算组合在不同的线程中(例如保持某种方法解决问题的概率的联合)。但这可能更多是可选项。
4.整个系统显然需要是线程安全的,并且我希望它能尽可能地快速运行。
我曾经尝试过一个涉及管理自己的线程并关闭它们等操作的实现,但它开始变得非常复杂,现在我想知道TPL是否更好。请问有人可以提供一些通用指导吗?
谢谢...
2个回答

4
我肯定会考虑使用TPL。它允许您抽象出问题。您可以考虑任务及其工作方式以及共享数据,而不是花费大量时间在底层线程模型上并创建自己的线程并管理它们。TPL将允许您创建任务,它会将任务分配给线程池。然后TPL管理池并调整运行任务的数量以最大化性能。它将在各种硬件配置(核心)上执行此操作,这使得开发应用程序更加容易,因为在不同的硬件之间移动时,不需要进行重大改写。
仍然有很多要考虑的事情,特别是围绕共享状态。除非您非常熟悉线程或具有某些特殊情况的应用程序,否则TPL通常比自己编写更好。
1.它们需要使用不同的“优化指标”进行初始化。换句话说,它们试图通过代码设置的优先级来优化不同的东西。这意味着它们都运行略有不同的计算引擎。我不确定是否可以使用TPL实现这一点。
您可以通过创建任务并传递它们不同的起始条件来实现此目的。
2.如果其中一个线程找到比当前已知的最佳解决方案更好的解决方案(需要在所有线程之间共享),那么它需要更新最佳解决方案,并强制一些其他线程重新启动(这取决于优化指标的优先级)。
可以取消任务并启动新任务。
3.我可能还希望在跨线程计算中组合某些计算(例如,保持对问题某种方法的概率的联合)。尽管如此,这可能更加自由选择。
我不确定是否理解了此要求。
4.整个系统显然需要是线程安全的,并且我希望它运行得尽可能快。
即使使用TPL跨任务(线程)共享数据,这仍然是您负责以线程安全的方式执行此操作。但是,TPL附带了几个线程安全类,例如队列、集合、包等。
听起来这是主/工作器模式的变体,其中还添加了一些推测执行和工作窃取。您可以在http://parallelpatterns.codeplex.com/上找到有关此模式和其他模式的更多详细信息,在页面底部有一个链接,指向Stephen Toub的白皮书,涵盖了更多详细信息。

0

无论如何,这都将变得复杂。编写适当的同步代码非常困难。我认为TPL对此来说有点过头了。

我的建议是放松一下,看看问题,白板记录并尽可能减少复杂性。

也许这会有所帮助...创建一个优化指标队列和一个共享类,其中包含最佳答案。使用读写锁保护共享类,使用互斥锁或其他锁保护队列。启动4-8个线程(每个CPU一个线程,如果您阻塞很多,则可以启动更多线程)并使它们在循环中运行。从队列中删除一个项目,处理它,检查共享数据,重复直到没有更多项目。

抵制启动3,000个线程的诱惑,并小心竞态条件,例如:获取共享类上的读取器锁,检查您的答案 - 假设它是更好的答案,请释放读取器锁并获取写入器锁并更新共享类。问题在于,当您等待写入器锁时,另一个线程可能已经更新了该类,并且一旦获得写入器锁,您就会立即将“最佳”答案清除而不进行检查。

玩得开心。


2
嗨Karl,谢谢回复。我喜欢TPL的一点是它可以自动地随着CPU和系统变化来扩展。这样我就不必监视线程性能等问题并相应地调整线程数量。我之前已经解决了很多问题,只有其中一些问题比较困难,很难在脑海中全部掌握,而且不想引入线程问题。 - Ian
我不确定为什么您认为线程需要这么多的开销。没有必要监视性能或其他任何事情,只需让它们继续运行,直到队列中没有更多元素并退出即可。您可以等待它们关闭,等待它们加入即可。for( int i = 0; i = Environment.ProcessorCount; i++ ) { ... 创建您的线程并将它们添加到列表中 ... }ThreadList.ForEach( T => { T.Join(); } );我认为您正在过度复杂化此问题。TPL就像用大锤敲碎核桃一样。但最终,您需要选择自己的毒药=) - Karl Strings
2
我不同意。你上面的代码并不是最理想的。你可能需要2-3倍于任务数量的线程来隐藏延迟。它也没有考虑到机器上除了你的应用程序之外的其他负载。TPL的默认调度程序将处理这两种情况。我真的建议在说它过于复杂之前认真看看它的功能。 - Ade Miller
  1. 我们都不知道什么是理想的,这应该由开发人员决定。
  2. 比任务更多的线程并不能隐藏任何东西。
  3. 我从未说过TPL过于复杂,你也不知道我是否认真看过TPL。
  4. 我说过线程很复杂。TPL不是万能药,它只是工具箱中的另一个工具。它有其适用的场合、目的和自己的一套优点、缺点和复杂性。
  5. 给开发人员提供可用的东西,而不仅仅是持不同意见和发布无意义的内容。
- Karl Strings
1
TYPO: “You probably want 2-3x the number of threads as tasks to hide latencies…” 应该改为 “You probably want 2-3x the number of threads than cores/hardware threads (ProcessorCount) to hide latencies (I/O, cache misses etc).” - Ade Miller
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接