CountedCompleter クラス
定義
重要
一部の情報は、リリース前に大きく変更される可能性があるプレリリースされた製品に関するものです。 Microsoft は、ここに記載されている情報について、明示または黙示を問わず、一切保証しません。
完了 ForkJoinTask
アクションがトリガーされたときに実行され、保留中のアクションが残っていない A。
[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
アクションがトリガーされたときに実行され、保留中のアクションが残っていない A。 CountedCompleters は、一般に、他の形式の ForkJoinTasks よりもサブタスクのストールとブロックが存在する場合の方が堅牢ですが、プログラムの直感的性は低くなります。 CountedCompleter の使用は、他の完了ベースのコンポーネント (などjava.nio.channels.CompletionHandler
) と似ていますが、完了アクション#onCompletion(CountedCompleter)
をトリガーするために複数<の em>保留中</em> 完了が必要になる場合がある点が異なります。 初期化されない限り、#getPendingCount 保留中のカウントは 0 から始まりますが、メソッド #setPendingCount
、 #addToPendingCount
および #compareAndSetPendingCount
. の呼び出し時に、保留中の #tryComplete
アクション数が 0 以外の場合はデクリメントされます。それ以外の場合は完了アクションが実行され、この完了者自体に完了がある場合は、その完了処理が続行されます。 このような関連する同期コンポーネント Phaser
の場合と Semaphore
同様に、これらのメソッドは内部カウントにのみ影響します。それ以上の内部ブックキーピングは確立されません。 特に、保留中のタスクの ID は保持されません。 次に示すように、必要に応じて保留中のタスクまたはその結果の一部またはすべてを記録するサブクラスを作成できます。 次に示すように、完了トラバーサルのカスタマイズをサポートするユーティリティ メソッドも提供されています。 ただし、CountedCompleters では基本的な同期メカニズムのみが提供されるため、関連する一連の使用法に適したリンケージ、フィールド、追加のサポート メソッドを維持する抽象サブクラスをさらに作成すると便利な場合があります。
具象 CountedCompleter クラスは、メソッド #compute
を定義する必要があります。ほとんどの場合 (次に示すように)、戻る前に 1 回呼び出す tryComplete()
必要があります。 クラスでは、通常の完了時にアクションを実行するメソッド #onCompletion(CountedCompleter)
と、例外時にアクションを実行するメソッド #onExceptionalCompletion(Throwable, CountedCompleter)
を、必要に応じてオーバーライドすることもできます。
CountedCompleters はほとんどの場合、結果を保持しません。その場合、通常は結果として宣言され CountedCompleter<Void>
、常に結果値として返 null
されます。 それ以外の場合は、メソッド #getRawResult
をオーバーライドして、関連するメソッドから join(), invoke()
結果を提供する必要があります。 通常、このメソッドは、完了時に結果を保持する CountedCompleter オブジェクトのフィールド (または 1 つ以上のフィールドの関数) の値を返す必要があります。 CountedCompleters では、既定ではメソッド #setRawResult
は役割を果たしません。 結果データを保持する他のオブジェクトまたはフィールドを維持するために、このメソッドをオーバーライドすることは可能ですが、ほとんど適用できません。
それ自体が完了していない CountedCompleter (つまり、返null
される関数) は、#getCompleter
この追加された機能を備えた通常の ForkJoinTask として使用できます。 ただし、別の完了者を持つすべての完了者は、他の計算の内部ヘルパーとしてのみ機能するため、(メソッドなどでForkJoinTask#isDone
報告される) 独自のタスクの状態は任意です。この状態は、明示的な呼び出し、ForkJoinTask#cancel
ForkJoinTask#completeExceptionally(Throwable)
またはメソッドcompute
の#complete
例外的な完了時にのみ変更されます。 例外的な完了時に、例外が存在し、それ以外の場合は完了していない場合は、タスクの完了者 (およびその完了者など) に中継される可能性があります。 同様に、CountedCompleter の内部を取り消す場合は、そのコンプリートに対してローカルな影響しか与えないため、あまり役に立ちません。
<b>サンプル使用法。</b>
<b>並列再帰分解。</b> CountedCompleters は、s でよく RecursiveAction
使用されるツリーと同様のツリーに配置できますが、通常、設定に関係する構造は異なります。 ここで、各タスクの完了者は計算ツリーの親です。 彼らは少し多くのブックキーピングを伴うが、CountedCompletersは、おそらく時間のかかる操作(それ以上分割することはできません)を配列またはコレクションの各要素に適用する際のより良い選択かもしれません。特に、組み込みのバリエーション (I/O など) やガベージ コレクションなどの補助効果が原因で、一部の要素に対して操作の完了に著しく異なる時間がかかる場合。 CountedCompleters は独自の継続を提供するため、他のタスクが実行を待機するのをブロックする必要はありません。
たとえば、2 除算再帰分解を使用して作業を 1 つの部分 (リーフ タスク) に分割するユーティリティ メソッドの初期バージョンを次に示します。 作業が個々の呼び出しに分割されている場合でも、通常、ツリーベースの手法は、スレッド間通信を減らし、負荷分散を向上させるので、リーフ タスクを直接フォークすることをお勧めします。 再帰的なケースでは、終了するサブタスクの各ペアの 2 番目のサブタスクは、親の完了をトリガーします (結果の組み合わせが実行されないため、メソッド onCompletion
の既定の no-op 実装はオーバーライドされません)。 ユーティリティ メソッドは、ルート タスクを設定し、それを呼び出します (ここでは、暗黙的に使用します 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();
}}
この設計を改善するには、再帰的なケースでは、タスクが右のタスクをフォークした後に何もする必要がないため、戻る前に左のタスクを直接呼び出すことができます。 (これは、テール 再帰の削除のアナログです)。また、タスクの最後のアクションがサブタスク ("テールコール") をフォークまたは呼び出す場合、保留中のカウントを "1 つオフ" にするというコストで、呼び出し 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();
}}
このようなクラスの追加の最適化には、リーフ ステップの特殊なクラスが必要になる場合があります。たとえば、イテレーションごとに 2 つではなく 4 つで分割し、常に単一の要素に分割するのではなく、アダプティブしきい値を使用します。
<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
である map-reduce の簡略化された形式を実行します)、除算および征服設計でこれを行う 1 つの方法は、各サブタスクがその兄弟を記録して、メソッド 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 回トリガーされます。保留中のカウントが 0 になる、または 0 になる 2 つの異なるコンテキストのいずれかで、保留中のカウントがタスク自体によってトリガーされます。(1) 保留中の tryComplete
カウントが呼び出されたときに 0 の場合、または (2) 完了して保留中のカウントを 0 にデクリメントするサブタスクのいずれかによってトリガーされます。 引数は caller
ケースを区別します。 ほとんどの場合、呼び出し元の場合、 this
アクションは必要ありません。 それ以外の場合は、呼び出し元の引数を (通常はキャストを介して) 使用して、結合する値 (および/または他の値へのリンク) を指定できます。 保留中のカウントを適切に使用すると、タスクとそのサブタスクが完了すると、内部 onCompletion
のアクションが (1 回) 発生します。 このメソッド内では、このタスクまたはその他の完了したタスクのフィールドへのアクセスのスレッド セーフを確保するために、追加の同期は必要ありません。
<b>完了トラバーサル。</b> 完了の処理に使用 onCompletion
する操作が適用できないか不便な場合は、メソッドを #firstComplete
使用して #nextComplete
カスタム トラバーサルを作成できます。 たとえば、3 番目の ForEach の例の形式で右側のタスクのみを分割する MapReducer を定義するには、次のように実行できる、不足していないサブタスク リンクに沿って補完を協調的に減らす必要があります。
{@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 の中には、それ自体がフォークされることはありませんが、代わりに他のデザインのプラミングのビットとして機能します。たとえば、1 つ以上の非同期タスクの完了によって別の非同期タスクがトリガーされるものも含まれます。 次に例を示します。
{@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 ドキュメントjava.util.concurrent.CountedCompleter
このページの一部は、Android オープンソース プロジェクトによって作成および共有され、クリエイティブ コモンズ 2.5 属性ライセンスに記載されている条件に従って使用される作業に基づく変更です。
コンストラクター
CountedCompleter() |
新しい CountedCompleter を作成します。完了者はなく、最初の保留中のカウントは 0 です。 |
CountedCompleter(CountedCompleter) |
新しい CountedCompleter を作成し、指定された完了者と最初の保留中のカウントを 0 に設定します。 |
CountedCompleter(CountedCompleter, Int32) |
指定された完了者と初期保留中のカウントを使用して、新しい CountedCompleter を作成します。 |
CountedCompleter(IntPtr, JniHandleOwnership) |
完了 |
プロパティ
Class |
この |
Completer |
このタスクのコンストラクターで確立されたコンプリートを返します。存在 |
Exception |
基本計算によってスローされた例外、または |
ForkJoinTaskTag |
このタスクのタグを返します。 (継承元 ForkJoinTask) |
Handle |
基になる Android インスタンスへのハンドル。 (継承元 Object) |
IsCancelled |
このタスクが |
IsCompletedAbnormally |
このタスクが |
IsCompletedNormally |
このタスクが例外を |
IsDone |
このタスクが |
JniIdentityHashCode |
完了 |
JniPeerMembers |
完了 |
PeerReference |
完了 |
PendingCount |
現在の保留中の数を返します。 または、保留中のカウントを指定された値に設定します。 |
RawRawResult |
このタスクが異常に完了した場合、または |
RawResult |
計算の結果を返します。 |
Root |
現在の計算のルートを返します。私。 |
ThresholdClass |
完了 |
ThresholdType |
完了 |
メソッド
AddToPendingCount(Int32) |
指定された値を保留中のカウントに (アトミックに) 追加します。 |
Cancel(Boolean) |
このタスクの実行を取り消そうとします。 (継承元 ForkJoinTask) |
Clone() |
このオブジェクトのコピーを作成して返します。 (継承元 Object) |
CompareAndSetForkJoinTaskTag(Int16, Int16) |
このタスクのタグ値をアトミックに条件付きで設定します。 (継承元 ForkJoinTask) |
CompareAndSetPendingCount(Int32, Int32) |
保留中のカウントを特定のカウントに (アトミックに) 設定するのは、現在指定された期待値を保持している場合のみです。 |
Complete(Object) |
保留中の数に関係なく、呼び出し |
CompleteExceptionally(Throwable) |
このタスクを異常終了し、まだ中止または取り消されていない場合は、指定された例外 |
Compute() |
このタスクによって実行される主な計算。 |
DecrementPendingCountUnlessZero() |
保留中のカウントが 0 以外の場合、(アトミックに) デクリメントされます。 |
Dispose() |
完了 |
Dispose(Boolean) |
完了 |
Equals(Object) |
他のオブジェクトがこのオブジェクトと "等しい" かどうかを示します。 (継承元 Object) |
Exec() |
CountedCompleters の実行規則を実装します。 |
FirstComplete() |
このタスクの保留中のカウントが 0 の場合は、このタスクを返します。それ以外の場合は、保留中のカウントをデクリメントして返します |
Fork() |
現在のタスクが実行されているプールで非同期的にこのタスクを実行するように配置します(該当する場合)。そうでない |
Get() |
計算が完了するまで必要に応じて待機し、その結果を取得します。 (継承元 ForkJoinTask) |
Get(Int64, TimeUnit) |
必要に応じて、計算が完了するまで最大で指定された時間待機し、その結果 (使用可能な場合) を取得します。 (継承元 ForkJoinTask) |
GetHashCode() |
オブジェクトのハッシュ コード値を返します。 (継承元 Object) |
HelpComplete(Int32) |
このタスクが完了していない場合は、このタスクが完了パス上にある他の未処理タスクの数 (存在することが判明している場合) を最大で処理しようとします。 |
Invoke() |
このタスクの実行を開始し、必要に応じて完了を待機し、その結果を返すか、(オフ) |
JavaFinalize() |
オブジェクトへの参照がなくなったとガベージ コレクションによって判断されたときに、オブジェクトのガベージ コレクターによって呼び出されます。 (継承元 Object) |
Join() |
#isDone が完了したときに計算の結果を返します。 (継承元 ForkJoinTask) |
NextComplete() |
このタスクに完了機能がない場合は、呼び出 |
Notify() |
このオブジェクトのモニターで待機している 1 つのスレッドを起動します。 (継承元 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() |
保留中のカウントが 0 以外の場合は、カウントをデクリメントします。それ以外の |
TryUnfork() |
このタスクの実行スケジュールの解除を試みます。 (継承元 ForkJoinTask) |
UnregisterFromRuntime() |
完了 |
Wait() |
現在のスレッドが目覚めるまで待機させます。通常<は、通知<>/em> または <em>割り込み/em> を受け<取ります。 (継承元 Object) |
Wait(Int64) |
現在のスレッドが目覚めるまで待機します。通常<><は、通知/em> または <em>中断</em> によって、または一定のリアルタイムが経過するまで待機します。 (継承元 Object) |
Wait(Int64, Int32) |
現在のスレッドが目覚めるまで待機します。通常<><は、通知/em> または <em>中断</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) |
完了 |