【问题标题】:Parallel programming. compute() method, java并行编程。计算()方法,java
【发布时间】:2016-08-25 10:10:31
【问题描述】:

我有这个类扩展RecursiveAction。 同一个类包含将数组作为参数和compute() 方法的构造函数。 在计算方法内部,它接下来说: 如果 array 长度大于 500,则将该数组分成两半并按 MergeSort.merge() 方法对其进行排序。如果数组长度小于 500,只需对 array 进行排序。

private static class SortTask extends RecursiveAction {
   private final int THRESHOLD = 500;
     private int[] list;
      SortTask(int[] list) {
       this.list = list;
         }
        @Override
protected void compute() {
if (list.length < THRESHOLD)
java.util.Arrays.sort(list);
else {
    // Obtain the first half
int[] firstHalf = new int[list.length / 2];
System.arraycopy(list, 0, firstHalf, 0, list.length / 2);


// Obtain the second half
int secondHalfLength = list.length - list.length / 2;
int[] secondHalf = new int[secondHalfLength];
System.arraycopy(list, list.length / 2,
secondHalf, 0, secondHalfLength);
// Recursively sort the two halves
invokeAll(new SortTask(firstHalf),
new SortTask(secondHalf));
// Merge firstHalf with secondHalf into list
MergeSort.merge(firstHalf, secondHalf, list);
}
}
}
}

它说 “递归地对两半进行排序”,它通过以下方式做到这一点:

invokeAll(new SortTask(firstHalf), new SortTask(secondHalf));

这是否意味着每次创建new SortTask 对象时,都会调用compute() 方法?

【问题讨论】:

  • 如果列表低于 THRESHOLD,则不会创建额外的 SortTask。否则,至少会创建一个额外的 SortTask 对(2、4、6、8...)。

标签: java data-structures mergesort


【解决方案1】:

答案是否定的。Compute 不是Task 构造函数的一部分。最简单的情况是池shutdownNow() - 此调用将尝试终止/中断所有提交的任务。

每次new SortTask 创建ForkJoinPool 都会派生这个任务:

public static void invokeAll(ForkJoinTask<?>... tasks) {
    Throwable ex = null;
    int last = tasks.length - 1;
    for (int i = last; i >= 0; --i) {
        ForkJoinTask<?> t = tasks[i];
        if (t == null) {
            if (ex == null)
                ex = new NullPointerException();
        }
        else if (i != 0)
            t.fork();
    ...
}

compute 方法是您的任务负载 - ForkJoinPool 将按照它的时间表进行。

来自ForkJoinTaskjavadoc:

主要的协调机制是 fork(),它安排 异步执行和 join(),直到 任务结果已计算完毕。

RecursiveTask 实现抽象方法:

protected final boolean exec() {
    result = compute();
    return true;
}

ForkJoinWorkerThread 有方法:

final void More execTask(ForkJoinTask<?> t) {
    currentSteal = t;
    for (;;) {
       if (t != null)
           t.doExec();
    ...
}

and at and doExec 调用 exec

final void doExec() {
    if (status >= 0) {
        boolean completed;
        try {
            completed = exec();
    ...
}

【讨论】:

  • invokeAll 在 compute() 中被调用,而 compute() 没有被显式调用。所以再一次,我想知道是否每次创建 SortTask 对象时都会调用 compute(),或者这不是真的
  • 要开始计算,您应该调用new ForkJoinPool().invoke(new SortTask(data));
  • 计算不是构造函数的一部分,但它在 RecursiveAction 类的实例化之后被调用。 Note 每次扩展 RecursiveAction 时都必须实现计算方法。但他的问题是它是否被调用,而不是它是否是它的一部分。
  • 想象ForkJoinPool 有一个工作线程。让我们有一个可以划分为 100 的任务。我们调用pool = new ForkJoinPool(Task),然后调用pool.shutdownNow(),在这种情况下,不会为所有子任务调用计算。
【解决方案2】:

是的,每次创建新的 SortTask 对象时,都会调用 compute() 方法,因为方法 invokeAll 在 ForkJoinPool 中分叉了一组任务,它隐式地为每个任务调用计算。

方法invoke()在语义上等价于fork(); join() 但总是尝试在当前线程中开始执行。

也来自文档:

通常,具体的 ForkJoinTask 子类 (RecursiveAction/RecursiveTask) 声明包含其参数的字段,在构造函数中建立,然后定义以某种方式使用控件的 compute 方法此基类提供的方法。

这里对它们进行排序的方法实际上是递归地划分它们,当它们达到THRESHOLD时将它们顺序排序,merge方法用于在它们排序后将它们合并。看看这个类似的例子:

class SortTask extends RecursiveAction {
final long[] array; final int lo; final int hi;
SortTask(long[] array, int lo, int hi) {
this.array = array; this.lo = lo; this.hi = hi;
}
protected void compute() {
 if (hi - lo < THRESHOLD)
   sequentiallySort(array, lo, hi);
  else {
   int mid = (lo + hi) >>> 1;
    coInvoke(new SortTask(array, lo, mid),
            new SortTask(array, mid+1, hi));
   merge(array, lo, hi);
       }
return null;
}
}

当coInvoke 在这里分叉两个任务并在isDone 为两者都成立时返回。

【讨论】:

  • 计算调用是异步的
  • @SergeyRybalkin 我写的是文档!,我仍然相信这个答案,所以我不会删除它。
猜你喜欢
  • 2016-08-23
  • 1970-01-01
  • 2020-11-22
  • 2014-04-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-26
  • 1970-01-01
相关资源
最近更新 更多