CountedCompleter 类
定义
重要
一些信息与预发行产品相关,相应产品在发行之前可能会进行重大修改。 对于此处提供的信息,Microsoft 不作任何明示或暗示的担保。
ForkJoinTask触发时执行完成操作且没有剩余挂起的操作。
[Android.Runtime.Register("java/util/concurrent/CountedCompleter", ApiSince=24, DoNotGenerateAcw=true)]
[Java.Interop.JavaTypeParameters(new System.String[] { "T" })]
public abstract class CountedCompleter : Java.Util.Concurrent.ForkJoinTask
[<Android.Runtime.Register("java/util/concurrent/CountedCompleter", ApiSince=24, DoNotGenerateAcw=true)>]
[<Java.Interop.JavaTypeParameters(new System.String[] { "T" })>]
type CountedCompleter = class
inherit ForkJoinTask
- 继承
- 属性
注解
ForkJoinTask触发时执行完成操作且没有剩余挂起的操作。 CountedCompleters 在子任务停止和阻塞的存在中通常更可靠,而不是其他类型的 ForkJoinTasks,但对编程不太直观。 CountedCompleter 的使用类似于其他基于完成的组件(例如 java.nio.channels.CompletionHandler)的用法,只是 <多个 em>挂起</em> 完成可能需要触发完成操作 #onCompletion(CountedCompleter),而不仅仅是一个。 除非另行初始化,否则 #getPendingCount 挂起的计数从零开始,但可以使用方法 #setPendingCount(以原子方式)更改, #addToPendingCount并且 #compareAndSetPendingCount。 #tryComplete调用时,如果挂起的操作计数为非零,则会递减;否则,将执行完成操作,如果此完成程序本身具有完整程序,则进程将继续完成。 与相关的同步组件(例如 Phaser ) Semaphore的情况一样,这些方法仅影响内部计数;它们不会建立任何进一步的内部记账。 具体而言,不会维护挂起任务的标识。 如下所示,可以创建子类,以便在需要时记录部分或所有挂起的任务或其结果。 如下所示,还提供了支持自定义完成遍历的实用工具方法。 但是,由于 CountedCompleters 仅提供基本同步机制,因此创建进一步的抽象子类可能很有用,这些子类维护链接、字段和其他支持方法适合一组相关用法。
具体 CountedCompleter 类必须定义方法 #compute,在大多数情况下(如下所示),在返回之前调用 tryComplete() 一次。 该类还可以选择性地重写在正常完成时执行操作的方法 #onCompletion(CountedCompleter) ,以及在任何异常时执行操作的方法 #onExceptionalCompletion(Throwable, CountedCompleter) 。
CountedCompleters 通常不承担结果,在这种情况下,它们通常声明为 CountedCompleter<Void>结果值,并且将始终作为结果值返回 null 。 在其他情况下,应重写方法 #getRawResult 以提供来自 join(), invoke()相关方法的结果。 通常,此方法应返回 CountedCompleter 对象的字段(或一个或多个字段的函数)的值,该对象在完成时保存结果。 默认情况下,方法 #setRawResult 在 CountedCompleters 中无作用。 可以(但很少适用)重写此方法来维护保存结果数据的其他对象或字段。
不具有完整(即#getCompleternull返回的 CountedCompleter)可用作具有此功能的常规 ForkJoinTask。 但是,反过来又具有另一个完成程序的任何完成程序仅作为其他计算的内部帮助程序,因此其自己的任务状态(如方法ForkJoinTask#isDone中报告)是任意的;此状态仅在显式调用 ForkJoinTask#completeExceptionally(Throwable) #completeForkJoinTask#cancel或异常完成方法compute时更改。 完成任何异常后,如果存在异常且尚未完成,则异常可以中继到任务的完成程序(及其完成程序等)。 同样,取消内部 CountedCompleter 只会对该完成程序产生本地影响,因此通常并不有用。
<b>示例用法。</b>
<b>并行递归分解。</b> CountedCompleters 可以排列在类似于通常用于 RecursiveActions 的树中,尽管设置它们所涉及的构造通常有所不同。 在这里,每个任务的完成者是计算树中的父任务。 即使它们需要更多的记账,CountedCompleters 在将可能耗时的操作(不能进一步细分)应用于数组或集合的每个元素时,可能更适合选择:尤其是当操作需要比其他元素完成的时间大相径庭时,无论是由于内部变化(例如 I/O)还是垃圾回收等辅助效果。 由于 CountedCompleters 提供自己的延续,因此其他任务无需阻止等待执行它们。
例如,下面是一个实用工具方法的初始版本,该方法使用除以二递归分解将工作划分为单个部分(叶任务)。 即使工作拆分为单个调用,基于树的技术通常更适合直接分叉叶任务,因为它们减少了线程间通信并提高负载均衡。 在递归的情况下,要完成其父级的触发器的每个子任务中的第二个(由于未执行任何结果组合,因此不会重写方法 onCompletion 的默认无操作实现)。 实用工具方法设置根任务并调用它(此处隐式使用 )。ForkJoinPool#commonPool() 始终将挂起计数设置为子任务数,并在返回之前立即调用 tryComplete() ,这一点非常简单且可靠(但不是最佳)。
{@code
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); this.lo = lo; this.hi = hi;
}
public void compute() {
if (hi - lo >= 2) {
int mid = (lo + hi) >>> 1;
// must set pending count before fork
setPendingCount(2);
new Task(this, mid, hi).fork(); // right child
new Task(this, lo, mid).fork(); // left child
}
else if (hi > lo)
action.accept(array[lo]);
tryComplete();
}
}
new Task(null, 0, array.length).invoke();
}}
通过指出,在递归的情况下,任务在分叉正确的任务后没有任何作用,因此可以在返回之前直接调用其左任务来改进此设计。 (这是尾递归删除的模拟。此外,当任务中的最后一个操作是分叉或调用子任务(“结尾调用”),可以优化调用 tryComplete() ,代价是使挂起的计数看起来“一个关闭”。
{@code
public void compute() {
if (hi - lo >= 2) {
int mid = (lo + hi) >>> 1;
setPendingCount(1); // looks off by one, but correct!
new Task(this, mid, hi).fork(); // right child
new Task(this, lo, mid).compute(); // direct invoke
} else {
if (hi > lo)
action.accept(array[lo]);
tryComplete();
}
}}
作为进一步优化,请注意,左侧任务甚至不存在。 我们可以继续使用原始任务,并为每个分叉添加挂起计数,而不是创建新任务。 此外,由于此树中没有任务实现方法 #onCompletion(CountedCompleter) , tryComplete 因此可以替换为 #propagateCompletion。
{@code
public void compute() {
int n = hi - lo;
for (; n >= 2; n /= 2) {
addToPendingCount(1);
new Task(this, lo + n/2, lo + n).fork();
}
if (n > 0)
action.accept(array[lo]);
propagateCompletion();
}}
当可以预计算挂起的计数时,可以在构造函数中建立它们:
{@code
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));
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();
}}
此类的其他优化可能需要专门对叶步骤的类进行细分,例如,四个,而不是每个迭代两个,并使用自适应阈值,而不是始终细分为单个元素。
<b>搜索。</b> CountedCompleters 的树可以在数据结构的不同部分搜索值或属性,并在找到结果后立即报告结果 java.util.concurrent.atomic.AtomicReference AtomicReference 。 其他人可以轮询结果以避免不必要的工作。 (还可以 #cancel 取消其他任务,但通常更简单、更高效地让他们注意到结果已设置,如果是,则跳过进一步处理。使用完整分区再次说明数组(实际上,叶任务几乎总是处理多个元素):
{@code
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);
new Searcher(this, array, result, mid, h).fork();
h = mid;
}
else {
E x = array[l];
if (matches(x) && result.compareAndSet(null, x))
quietlyCompleteRoot(); // root task is now joinable
break;
}
}
tryComplete(); // normally complete whether or not found
}
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();
}
}}
在此示例中,除了任务没有其他效果的其他任务,除了 compareAndSet 常见结果之外,可以有条件地调用尾随无条件调用 tryComplete (if (result.get() == null) tryComplete();),因为在根任务完成后,无需进一步的记账来管理完成。
<b>录制子任务。</b> CountedCompleter 任务,合并多个子任务的结果通常需要在方法 #onCompletion(CountedCompleter)中访问这些结果。 如以下类所示(执行一种简化形式的地图化简,其中映射和缩减都是所有类型的 E),一种方法是在分割和征服设计中执行此操作,即让每个子任务记录其同级,以便可以在方法 onCompletion中访问它。 此方法适用于合并左右结果的顺序无关紧要的减少:有序减少需要显式的向左/右指定。 上述示例中显示的其他精简方法的变体也可能适用。
{@code
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); // only right is pending
right.fork();
left.compute(); // directly execute left
}
else {
if (hi > lo)
result = mapper.apply(array[lo]);
tryComplete();
}
}
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();
}
}}
在这里,方法 onCompletion 采用许多组合结果的完成设计共有的形式。 此回调样式方法在每个任务触发一次,在两个不同的上下文中,其中一个不同上下文中挂起计数为零:(1)由任务本身触发:(1)如果任务在调用时挂起的计数为零,则其任何子任务在完成时由其任何子任务触发 tryComplete,并将挂起计数递减为零。 该 caller 参数区分大小写。 大多数情况下,调用方 this不需要执行任何操作。 否则,可以使用调用方参数(通常通过强制转换)提供要组合的值(和/或指向其他值的链接)。 假设正确使用挂起计数,则任务及其子任务完成后(一次)内的操作 onCompletion 就会发生。 此方法中不需要任何其他同步,以确保访问此任务或其他已完成任务的字段的线程安全。
<b>完成遍历。</b> 如果用于onCompletion处理完成操作不可应用或不方便,则可以使用方法和#firstComplete#nextComplete创建自定义遍历。 例如,若要定义一个 MapReducer,它只以第三个 ForEach 示例的形式拆分出右手任务,则完成必须协作减少未用完的子任务链接,可按如下所示完成:
{@code
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; // record subtask forks in list
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);
(forks = new MapReducer(this, array, mapper, reducer, mid, h, forks)).fork();
h = mid;
}
if (h > l)
result = mapper.apply(array[l]);
// process completions by reducing along and advancing subtask links
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();
}
}}
<b>触发器。</b> 某些 CountedCompleters 本身从未分叉,而是在其他设计中充当管道的位;包括完成一个或多个异步任务会触发另一个异步任务。 例如:
{@code
class HeaderBuilder extends CountedCompleter<...> { ... }
class BodyBuilder extends CountedCompleter<...> { ... }
class PacketSender extends CountedCompleter<...> {
PacketSender(...) { super(null, 1); ... } // trigger on second completion
public void compute() { } // never called
public void onCompletion(CountedCompleter<?> caller) { sendPacket(); }
}
// sample use:
PacketSender p = new PacketSender();
new HeaderBuilder(p, ...).fork();
new BodyBuilder(p, ...).fork();}
已在 1.8 中添加。
适用于 . 的 java.util.concurrent.CountedCompleterJava 文档
本页的某些部分是根据 Android 开放源代码项目创建和共享的工作进行的修改,并根据 Creative Commons 2.5 属性许可证中所述的术语使用。
构造函数
| CountedCompleter() |
创建一个新的 CountedCompleter,其中没有完成器,初始挂起计数为零。 |
| CountedCompleter(CountedCompleter) |
使用给定的完成器创建新的 CountedCompleter,初始挂起计数为零。 |
| CountedCompleter(CountedCompleter, Int32) |
使用给定的完整和初始挂起计数创建新的 CountedCompleter。 |
| CountedCompleter(IntPtr, JniHandleOwnership) |
|
属性
| Class |
返回此 |
| Completer |
返回在此任务的构造函数中建立的完成程序;如果没有 |
| Exception |
返回基计算引发的异常; |
| ForkJoinTaskTag |
返回此任务的标记。 (继承自 ForkJoinTask) |
| Handle |
基础 Android 实例的句柄。 (继承自 Object) |
| IsCancelled |
返回 |
| IsCompletedAbnormally |
如果此任务引发异常或已取消,则返回 |
| IsCompletedNormally |
返回 |
| IsDone |
返回 |
| JniIdentityHashCode |
|
| JniPeerMembers |
|
| PeerReference |
|
| PendingCount |
返回当前挂起计数。 - 或 - 将挂起的计数设置为给定的值。 |
| RawRawResult |
返回将返回 Join()的结果,即使此任务异常完成,或者 |
| RawResult |
返回计算结果。 |
| Root |
返回当前计算的根;我。 |
| ThresholdClass |
|
| ThresholdType |
|
方法
| AddToPendingCount(Int32) |
将给定值(原子方式)添加到挂起的计数。 |
| Cancel(Boolean) |
尝试取消执行此任务。 (继承自 ForkJoinTask) |
| Clone() |
创建并返回此对象的副本。 (继承自 Object) |
| CompareAndSetForkJoinTaskTag(Int16, Int16) |
以原子方式设置此任务的标记值。 (继承自 ForkJoinTask) |
| CompareAndSetPendingCount(Int32, Int32) |
仅当挂起的计数当前保留给定的预期值时,才会将挂起的计数设置为给定计数。 |
| Complete(Object) |
无论挂起计数如何,调用 |
| CompleteExceptionally(Throwable) |
异常完成此任务,如果尚未中止或取消,则会导致它引发 |
| Compute() |
此任务执行的主要计算。 |
| DecrementPendingCountUnlessZero() |
如果挂起的计数为非零,则(原子上)会递减它。 |
| Dispose() |
|
| Dispose(Boolean) |
|
| Equals(Object) |
指示其他对象是否“等于”此对象。 (继承自 Object) |
| Exec() |
实现 CountedCompleters 的执行约定。 |
| FirstComplete() |
如果此任务的挂起计数为零,则返回此任务;否则会递减其挂起计数并返回 |
| Fork() |
排列在池中异步执行此任务,当前任务在池中运行(如果适用)或使用 |
| Get() |
根据需要等待计算完成,然后检索其结果。 (继承自 ForkJoinTask) |
| Get(Int64, TimeUnit) |
如果需要,最多等待给定时间计算完成,然后检索其结果(如果可用)。 (继承自 ForkJoinTask) |
| GetHashCode() |
返回对象的哈希代码值。 (继承自 Object) |
| HelpComplete(Int32) |
如果此任务尚未完成,则尝试处理此任务位于完成路径的其他未处理任务的给定数目(如果已知存在)。 |
| Invoke() |
开始执行此任务,如有必要,等待其完成,并返回其结果,或引发(未检查) |
| JavaFinalize() |
当垃圾回收确定不再引用该对象时,由对象上的垃圾回收器调用。 (继承自 Object) |
| Join() |
在计算完成 #isDone 时返回计算结果。 (继承自 ForkJoinTask) |
| NextComplete() |
如果此任务没有完整程序,则 |
| Notify() |
唤醒正在等待此对象的监视器的单个线程。 (继承自 Object) |
| NotifyAll() |
唤醒正在等待此对象的监视器的所有线程。 (继承自 Object) |
| OnCompletion(CountedCompleter) |
在调用方法 |
| OnExceptionalCompletion(Throwable, CountedCompleter) |
调用方法 |
| PropagateCompletion() |
等效于 |
| QuietlyComplete() |
通常无需设置值即可完成此任务。 (继承自 ForkJoinTask) |
| QuietlyCompleteRoot() |
等效于 |
| QuietlyInvoke() |
开始执行此任务,并在必要时等待其完成,而不返回其结果或引发异常。 (继承自 ForkJoinTask) |
| QuietlyJoin() |
联接此任务,而不返回其结果或引发异常。 (继承自 ForkJoinTask) |
| Reinitialize() |
重置此任务的内部记帐状态,允许后续 |
| SetForkJoinTaskTag(Int16) |
以原子方式设置此任务的标记值并返回旧值。 (继承自 ForkJoinTask) |
| SetHandle(IntPtr, JniHandleOwnership) |
设置 Handle 属性。 (继承自 Object) |
| SetRawResult(Object) |
结果承载 CountedCompleters 的方法可以选择用于帮助维护结果数据。 |
| ToArray<T>() |
|
| ToString() |
返回对象的字符串表示形式。 (继承自 Object) |
| TryComplete() |
如果挂起的计数为非零,则递减计数;否则调用 |
| TryUnfork() |
尝试取消计划此任务以执行。 (继承自 ForkJoinTask) |
| UnregisterFromRuntime() |
|
| Wait() |
使当前线程等待,直到唤醒它,通常是通过 em 通知/em> 或 <em>interrupted</em>。<>< (继承自 Object) |
| Wait(Int64) |
使当前线程等待直到唤醒,通常是通过 <em>通知</em> 或 <em interrupted</em>>,或直到经过一定数量的实时。 (继承自 Object) |
| Wait(Int64, Int32) |
使当前线程等待直到唤醒,通常是通过 <em>通知</em> 或 <em interrupted</em>>,或直到经过一定数量的实时。 (继承自 Object) |
显式接口实现
| IJavaPeerable.Disposed() |
|
| IJavaPeerable.DisposeUnlessReferenced() |
|
| IJavaPeerable.Finalized() |
|
| IJavaPeerable.JniManagedPeerState |
|
| IJavaPeerable.SetJniIdentityHashCode(Int32) |
|
| IJavaPeerable.SetJniManagedPeerState(JniManagedPeerStates) |
|
| IJavaPeerable.SetPeerReference(JniObjectReference) |
|
扩展方法
| JavaCast<TResult>(IJavaObject) |
执行 Android 运行时检查的类型转换。 |
| JavaCast<TResult>(IJavaObject) |
|
| GetJniTypeName(IJavaPeerable) |
|
| GetAsync(IFuture) |
|
| GetAsync(IFuture, Int64, TimeUnit) |
|