[英]Recycling threads to increase parallelism in recursive calls in C#
我在C#中有一个任务,需要递归计算树状结构中项目的属性。 基本上,我只是进行递归深度优先搜索,但是使用线程来并行化过程。 这意味着,从树的根开始,我启动N
线程,为根的每个子项分配一个(最多可用线程,我们将其称为L
)。 因此,如果N
严格大于L
,则我将等待(N - L)
根子节点等待处理,随着功能完全返回,这些子节点最终会逐渐发生,线程终止并回收用于处理下一个根子元素(如果还有任何未处理的元素)。
我只是在考虑一个或多个原始线程的运行时间相对较长的场景,以至于其他根子代(及其子代)在此之前或某个线程有时间完成之前有时间要处理。 这意味着我将不执行任何工作而具有潜在的可工作线程。
我想要实现的是实际使用这些线程来加速运行时间更长的执行路径。 我在考虑Open MPI ,实际上您可以始终使所有可用线程动态地处理其余任务。 这意味着,在每次调用递归方法之前,我都应该以某种方式检查是否所有根子对象都已被处理(因为它们是优先级),并且在这种情况下,生成递归调用的线程版本,然后移动继续下一个。
我试图在下面的图表(很差)中说明这种行为(其中每个横向分支都是递归方法调用/返回)。 第一张图片显示了thread2
完成速度比thread1
快得多的情况,然后只是简单地躺在周围而没有任何其他贡献。 第二张图片显示了所需的行为,在该行为中,在进行下一个递归调用之前,我们实际上使用可用线程将其线程化,然后继续下一个子级。 n级部分只是意味着它可以在递归中更深入。
您是否知道有什么方法可以在C#中促进此过程? 关于代码的并行化(就构造,数据结构等而言),最好的策略是什么? 另外,是否有一种简单的方法可以“减少”(汇总)计算结果?
只需使用线程池,而不是创建自己的线程。 让每个节点由一个线程池任务处理。 然后,线程池将处理创建/删除线程,以便它有足够的线程来处理分配给它的操作的平均吞吐量,而不会使线程长时间闲置。
我认为您应该考虑将TPL库用于这种方法,可能与您自己实现的TaskScheduler
类一起使用。 我认为这将适合您,因为:
Tpl库在内部使用ThreadPool
因此您无需担心系统资源管理。 默认的任务计划程序基于.NET Framework 4 ThreadPool,它提供了用于负载平衡的工作窃取 。
您可以将子任务附加到父任务 ,这将覆盖算法的递归部分,如下所示:
using System; using System.Threading; using System.Threading.Tasks; public class Example { public static void Main() { var parent = Task.Factory.StartNew(() => { Console.WriteLine("Parent task executing."); var child = Task.Factory.StartNew(() => { Console.WriteLine("Attached child starting."); Thread.SpinWait(5000000); Console.WriteLine("Attached child completing."); }, TaskCreationOptions.AttachedToParent); }); parent.Wait(); Console.WriteLine("Parent has completed."); } } // The example displays the following output: // Parent task executing. // Attached child starting. // Attached child completing. // Parent has completed.
可以通过CancellationToken
任务,因此您可以轻松删除完整的子级集合以返回到第一级任务。 您也可以实现自己的TaskScheduler
并使用它来确定下一个应该运行的任务。 有关如何创建和使用自定义任务计划程序的更多信息,请参见如何:创建限制并发性的任务计划程序 。 有关自定义调度程序的其他示例,请参见MSDN Code Gallery网站上的Parallel Extensions Samples 。
System.Collections.Concurrent
命名空间中的几个线程安全,快速和可伸缩的集合类,以及几个新的同步类型,例如System.Threading.Semaphore
和System.Threading.ManualResetEventSlim
,它们比以前的版本更高效。特定类型的工作负载。 .NET Framework 4中的其他新类型,例如System.Threading.Barrier
和System.Threading.SpinLock
,提供了早期版本中不提供的功能。 有关更多信息,请参见《并行编程的数据结构》 。 async/await
,因此您的代码不仅可以并行执行,而且可以异步执行。 aggregate
意味着什么,但是我认为TPL Dataflow
命名空间可以为您提供帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.