CountedCompleter使用方法小洁

CountedCompleter将结果合并从任务分解计算的过程中分离出来,只要每个子任务能保存状态,那么通过合并逻辑,就可以在任何时候将结果合并。

CountedCompleter中如何知道子任务计算完成?它使用了一个孩子兄弟表示的树形结构。用一个变量统计当前一个节点的子节点个数,兄弟节点通过判断计算器可以知道其他兄弟任务是否完成,如果没有完成就将计数器减一表示自己已经完成。如果完成了就将所有兄弟任务的结果合并(合并顺序可以自定义),然后递归判断父节点的情况。这样最终就可以将任务结果合并到根节点。

因此只要理解CountedCompleter的执行流程,就可以知道使用CountedCompleter其实是很简单的:

1、创建子任务,创建子任务首先将父任务的pending计数器加1,然后保存子任务到兄弟节点可以访问到的地方,以便之后能进行合并。

2、任务完成,任务完成时调用tryComplete合并结果,(可以合并时会调用onCompletion合并结果)

CountedCompleter除了可以像上面这种mapreduce的使用方法之外也有其他使用场景,例如不需要结果、find Any等,实现方式都大致相同。

下面就是代码环节,直接使用源码上的例子加上一些自己的注释理解。

无结果模板,这里通过直接计算子任务的数量一次性设置了父任务的pending值。

public static <E> void forEach(E[] array, Consumer<E> action) {
    // 匿名类
   class Task extends CountedCompleter<Void> {
     final int lo, hi;
     Task(Task parent, int lo, int hi) {
       super(parent, 31 - Integer.numberOfLeadingZeros(hi - lo)); // logn
       this.lo = lo; this.hi = hi;
     }

     public void compute() {
       for (int n = hi - lo; n >= 2; n /= 2)
         new Task(this, lo + n/2, lo + n).fork(); // 提交子任务
       action.accept(array[lo]);
       propagateCompletion();
     }
   }
   if (array.length > 0)
     new Task(null, 0, array.length).invoke(); // 任务入口
 }

 

findany模板

class Searcher<E> extends CountedCompleter<E> {
   final E[] array; final AtomicReference<E> result; final int lo, hi;
   Searcher(CountedCompleter<?> p, E[] array, AtomicReference<E> result, int lo, int hi) {
     super(p);
     this.array = array; this.result = result; this.lo = lo; this.hi = hi;
   }
   public E getRawResult() { return result.get(); }
   public void compute() { // similar to ForEach version 3
     int l = lo, h = hi;
     while (result.get() == null && h >= l) {
       if (h - l >= 2) {
         int mid = (l + h) >>> 1;
         addToPendingCount(1); // 创建右子任务,父任务计数器加1
         new Searcher(this, array, result, mid, h).fork(); // 提交右子任务
         h = mid; // 当前线程计算左子任务,MapReduce里面是递归写法,这里是循环写法
       }
       else {
         E x = array[l];
         if (matches(x) && result.compareAndSet(null, x))
           quietlyCompleteRoot(); // 找到解,设置结果,并且设置任务为完成状态,用户根据通过根节点获得解
         break;
       }
     }
     tryComplete(); // 任务完成,没有解也得完成啊
   }
   boolean matches(E e) { ... } // return true if found

   public static <E> E search(E[] array) {
       return new Searcher<E>(null, array, new AtomicReference<E>(), 0, array.length).invoke();
   }
 }

mapreduce的示例,通过递归实现只有两个子任务

 

class MyMapper<E> { E apply(E v) {  ...  } }
 class MyReducer<E> { E apply(E x, E y) {  ...  } }
class MapReducer<E> extends CountedCompleter<E> {
    final E[] array; final MyMapper<E> mapper;
    final MyReducer<E> reducer; final int lo, hi;
    MapReducer<E> sibling; // 将相邻的节点当成兄弟节点,只有两个子任务
    E result; // 结果
    MapReducer(CountedCompleter<?> p, E[] array, MyMapper<E> mapper,
               MyReducer<E> reducer, int lo, int hi) {
      super(p);
      this.array = array; this.mapper = mapper;
      this.reducer = reducer; this.lo = lo; this.hi = hi;
    }
    public void compute() {
      if (hi - lo >= 2) {
        int mid = (lo + hi) >> 1;
        MapReducer<E> left = new MapReducer(this, array, mapper, reducer, lo, mid);
        MapReducer<E> right = new MapReducer(this, array, mapper, reducer, mid, hi);
        left.sibling = right;
        right.sibling = left;
        setPendingCount(1); // 创建子任务父任务的pending计数器加1
        right.fork(); // 提交右子任务
        left.compute();     // 在当前线程计算左子任务
      }
      else {
        if (hi > lo)
            result = mapper.apply(array[lo]); 
        tryComplete(); // 叶子节点完成,尝试合并其他兄弟节点的结果,会调用onCompletion方法
      }
    }
    public void onCompletion(CountedCompleter<?> caller) {
      if (caller != this) {
        MapReducer<E> child = (MapReducer<E>)caller;
        MapReducer<E> sib = child.sibling;
        // 合并子任务结果,只有两个子任务
        if (sib == null || sib.result == null)
          result = child.result;
        else
          result = reducer.apply(child.result, sib.result);
      }
    }
    public E getRawResult() { return result; }
 
    public static <E> E mapReduce(E[] array, MyMapper<E> mapper, MyReducer<E> reducer) {
      return new MapReducer<E>(null, array, mapper, reducer,
                               0, array.length).invoke();
    }
}}

MapReduce模板,通过循环实现有多个子任务

 

class MapReducer<E> extends CountedCompleter<E> { // version 2
   final E[] array; final MyMapper<E> mapper;
   final MyReducer<E> reducer; final int lo, hi;
   MapReducer<E> forks, next; // 保存多个子任务
   E result;
   MapReducer(CountedCompleter<?> p, E[] array, MyMapper<E> mapper,
              MyReducer<E> reducer, int lo, int hi, MapReducer<E> next) {
     super(p);
     this.array = array; this.mapper = mapper;
     this.reducer = reducer; this.lo = lo; this.hi = hi;
     this.next = next;
   }
   public void compute() {
     int l = lo, h = hi;
     while (h - l >= 2) {
       int mid = (l + h) >>> 1;
       addToPendingCount(1); // 创建子任务,父任务计数器加1
       (forks = new MapReducer(this, array, mapper, reducer, mid, h, forks)).fork();  // 提交子任务
       h = mid; // 循环处理左区间
     }
     if (h > l)
       result = mapper.apply(array[l]);
     // 按照子任务链合并兄弟节点,然后再合并父节点那一层的结果,直到合并完全部结果。和tryComplete()+onCompletion()的作用一样
     for (CountedCompleter<?> c = firstComplete(); c != null; c = c.nextComplete()) {
       for (MapReducer t = (MapReducer)c, s = t.forks; s != null; s = t.forks = s.next)
         t.result = reducer.apply(t.result, s.result);
     }
   }
   public E getRawResult() { return result; }

   public static <E> E mapReduce(E[] array, MyMapper<E> mapper, MyReducer<E> reducer) {
     return new MapReducer<E>(null, array, mapper, reducer,
                              0, array.length, null).invoke();
   }
 }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值