我该如何写任务?(并行代码)

11 parallel-processing tbb

英特尔线程构建块给我留下了深刻的印象.我喜欢我应该如何编写任务而不是线程代码,我喜欢它如何在我的有限理解下工作(任务在池中,4核上不会有100个线程,任务不能保证运行,因为它不是它自己的线程可能会进入池中.但它可能与另一个相关的任务一起运行,所以你不能做坏事,如典型的线程不安全的代码).

我想更多地了解写作任务.我喜欢这里的"基于任务的多线程 - 如何为100个内核编程"视频http://www.gdcvault.com/sponsor.php?sponsor_id=1(目前是第二个最后一个链接.警告它不是'很棒').我最喜欢的部分是"解决迷宫最好并行完成",大约是48分钟(你可以点击左侧的链接.如果有的话,这部分真的是你需要观察的).

但是,我希望看到更多的代码示例和一些如何编写任务的API.有没有人有很好的资源?我不知道一个类或多个代码片段在将它推入池中后可能看起来如何或当你需要复制所有内容以及将所有内容推送到池中时,它们看起来有多奇怪.

mdm*_*dma 7

Java有一个类似于Thread Building Blocks的并行任务框架 - 它被称为Fork-Join框架.它可以与当前的Java SE 6一起使用,并包含在即将发布的Java SE 7中.

除了javadoc类文档之外,还有可用于开始使用框架的资源.在jsr166页面中,提到"还有一个wiki包含这些类的附加文档,注释,建议,示例等等."

叉连接的例子,如矩阵乘法是一个良好的开端.

我使用fork-join框架来解决英特尔2009年的一些线程挑战.该框架重量轻,开销低 - 我是Kight's Tour问题的唯一Java条目,并且在竞争中表现优于其他条目.可以从挑战站点下载java源和写入以供下载.

编辑:

我不知道一个或多个代码在将它推入池后会如何看待[...]

您可以通过继承其中一个ForKJoinTask子类(如RecursiveTask)来创建自己的任务.以下是如何并行计算斐波纳契数列.(取自RecursiveTaskjavadocs - 评论是我的.)

 // declare a new task, that itself spawns subtasks. 
 // The task returns an Integer result.
 class Fibonacci extends RecursiveTask<Integer> {
   final int n;      // the n'th number in the fibonacci sequence to compute
   Fibonnaci(int n) { this.n = n; } // constructor
   Integer compute() {   // this method is the main work of the task
     if (n <= 1)         // 1 or 0, base case to end recursion
        return n;
     Fibonacci f1 = new Fibonacci(n - 1);  // create a new task to compute n-1
     f1.fork();                            // schedule to run asynchronously
     Fibonacci f2 = new Fibonacci(n - 2);  // create a new task to compute n-2
     return f2.invoke() + f1.join();       // wait for both tasks to compute.
       // f2 is run as part of this task, f1 runs asynchronously. 
       // (you could create two separate tasks and wait for them both, but running
       // f2 as part of this task is a little more efficient.
   }
 }
Run Code Online (Sandbox Code Playgroud)

然后运行此任务并获得结果

// default parallelism is number of cores
ForkJoinPool pool = new ForkJoinPool(); 
Fibonacci f = new Fibonacci(100);
int result = pool.invoke(f);
Run Code Online (Sandbox Code Playgroud)

这是让事情变得简单的一个简单例子.在实践中,性能不会那么好,因为与任务框架的开销相比,任务执行的工作是微不足道的.根据经验,任务应该执行一些重要的计算 - 足以使框架开销无关紧要,但并不是说在运行一个大任务的问题结束时你最终得到一个核心.将大型任务拆分为较小的任务可确保一个核心在其他核心处于空闲状态时不会做大量工作 - 使用较小的任务可以保持更多核心繁忙,但不能太小,以至于任务无法正常工作.

[...]或者当你需要复制所有内容以及将所有内容推送到池中时,可能会看到奇怪的代码.

只有任务本身被推入池中.理想情况下,您不希望复制任何内容:为了避免干扰和锁定的需要,这会降低程序的速度,理想情况下您的任务应该使用独立的数据.只读数据可以在所有任务之间共享,不需要复制.如果线程需要合作构建一些大型数据结构,那么最好分别构建它们,然后在最后组合它们.组合可以作为单独的任务完成,或者每个任务可以将其中的一部分添加到整体解决方案中.这通常需要某种形式的锁定,但如果任务的工作量远远大于更新解决方案的工作,那么这不是一个相当大的性能问题.我的骑士'

使用任务和并发性是常规单线程编程的范例转换.通常有几种设计可以解决给定的问题,但其中只有一些适用于线程解决方案.可能需要一些尝试来了解如何以多线程方式重新熟悉问题.学习的最佳方法是查看示例,然后亲自尝试.总是分析,并考虑改变线程数量的影响.您可以在池构造函数中显式设置池中使用的线程数(核心数).当任务线性分解时,随着线程数量的增加,您可以预期接近线性加速.