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)類似,但觸發完備動作<可能需要多個 >em<待處理>/完成#onCompletion(CountedCompleter),而非僅一個。 除非另有初始化,待處理計數 #getPendingCount 從零開始,但可透過 #setPendingCount、 #addToPendingCount、 #compareAndSetPendingCount和 的方法(原子層面)更改。 在呼叫 時 #tryComplete,若待處理的動作計數非零,則會遞減;否則執行完備動作,且若該完成器本身有完備者,則該程序會以其完備者繼續。 與相關的同步元件 Phaser 如 和 Semaphore一樣,這些方法僅影響內部計數;它們不會建立任何進一步的內部帳務。 特別是,待處理任務的身份不會被維護。 如下圖所示,你可以建立子類別,在需要時記錄部分或全部待處理任務或其結果。 如下所示,也提供了支援完成遍歷自訂的工具方法。 然而,由於 CountedCompleters 僅提供基本的同步機制,可能有助於建立更多抽象子類別,以維持連結、欄位及適合相關用途的額外支援方法。
一個具體的 CountedCompleter 類別必須定義方法,該方法 #compute在大多數情況下(如下所示)應先 tryComplete() 呼叫一次後返回。 該類別也可以選擇覆蓋方法 #onCompletion(CountedCompleter) ,使其在正常完成時執行動作,以及 #onExceptionalCompletion(Throwable, CountedCompleter) 方法在任意例外時執行動作。
CountedCompletionrs 通常不會產生結果,這時通常會宣告為 CountedCompleter<Void>,並且總是以結果值的形式回傳 null 。 在其他情況下,你應該覆寫方法 #getRawResult 以提供來自 join(), invoke()及相關方法的結果。 一般而言,此方法應回傳 CountedCompleter 物件中一個欄位(或一個或多個欄位的函數)的值,該欄位在完成時會保留該結果。 方法 #setRawResult 預設在 CountedCompletionrs 中不扮演角色。 雖然可以,但很少適用,可以覆寫此方法以維護其他物件或欄位中儲存結果資料。
一個本身沒有完整運算器的 CountedCompleter(即 #getCompleter 回傳 null的 ),可作為附加此功能的一般 ForkJoinTask 使用。 然而,任何有另一個完備器的補全器僅作為其他計算的內部輔助者,因此其自身的任務狀態(如 ForkJoinTask#isDone)是任意的;此狀態僅在明確呼叫 #complete、 ForkJoinTask#cancel或 ForkJoinTask#completeExceptionally(Throwable) 方法異常完成 compute時改變。 在任何異常完成時,該例外可能會被轉發給任務的完成者(如此類推),前提是任務已存在且尚未完成。 同樣地,取消內部的 CountedCompleter 只會對該完整器產生局部影響,因此通常沒什麼用處。
<b>範例用法。</b>
<b>平行遞迴分解。</b> CountedCompleters 可以排列成類似於常用 s 的 RecursiveAction樹狀結構,儘管設置時涉及的結構通常會有所不同。 此處,每個任務的完成者是其計算樹中的父節點。 雖然 CountedCompleters 需要較多的簿記,但在對陣列或集合的每個元素應用可能耗時(且無法進一步細分)的操作時,可能是更好的選擇;尤其是當某些元素所需的時間與其他元素有顯著差異時,無論是因為內在變異(例如 I/O)或輔助效應如垃圾回收。 由於 CountedCompletionrs 自行提供續接,其他任務不必阻塞等待來執行。
例如,這裡有一個利用工具方法的初始版本,利用除以二的遞迴分解將工作分割成單一部分(葉節點任務)。 即使工作被拆分成個別呼叫,基於樹狀的技術通常也比直接分支葉節點任務更可取,因為它們減少執行緒間通訊並改善負載平衡。 在遞迴情況下,每對子任務中第二個完成的子任務會觸發其父任務的完成(因為沒有執行結果組合,方法的預設 no-op 實作 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> CountedCompletionr 任務結合多個子任務結果,通常需要在方法 #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) 由任務本身觸發,若其待處理計數在呼叫 tryComplete時為零;(2) 由其任一子任務在完成並將待處理計數減少為零時觸發。 此 caller 論點區分案例。 大多數情況下,當呼叫者是 this時,不需要採取任何行動。 否則,呼叫者參數(通常透過鑄造)可用來提供一個值(及/或連結其他值)以便合併。 假設正確使用待處理計數,內部 onCompletion 的動作會在完成任務及其子任務時(僅發生一次)。 此方法中不需要額外的同步,以確保對該任務或其他已完成任務欄位存取的執行緒安全。
<b>完成遍歷。</b> 如果用 onCompletion 來處理補全不適用或不方便,你可以使用方法 #firstComplete 並 #nextComplete 建立自訂遍歷。 例如,要定義一個僅以第三個 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 本身從未被分叉,而是在其他設計中作為管道的 bits;包括完成一個或多個非同步任務會觸發另一個非同步任務的系統。 例如:
{@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。
本頁部分內容為基於 Open Source Project 所創建與分享的作品,並依授權條款所描述的使用進行修改。
建構函式
| 名稱 | Description |
|---|---|
| CountedCompleter() |
建立一個新的 CountedCompleter,沒有補全器,初始待處理計數為零。 |
| CountedCompleter(CountedCompleter, Int32) |
建立一個新的 CountedCompleter,包含給定的 completer 和初始待處理計數。 |
| CountedCompleter(CountedCompleter) |
建立一個新的 CountedCompleter,包含給定的 completer 和初始待處理計數為零。 |
| CountedCompleter(IntPtr, JniHandleOwnership) |
|
屬性
| 名稱 | Description |
|---|---|
| Class |
回傳此 |
| Completer |
回傳本任務建構子中建立的完成符,若 |
| Exception |
回傳基礎計算拋出的例外,若被取消則 |
| ForkJoinTaskTag |
會回傳此任務的標籤。 (繼承來源 ForkJoinTask) |
| Handle |
底層 Android 實例的帳號。 (繼承來源 Object) |
| IsCancelled |
如果此任務在正常完成前被取消,則會回傳 |
| IsCompletedAbnormally |
如果此任務拋出例外或被取消,則會回傳 |
| IsCompletedNormally |
如果該任務完成且未拋出例外且未被取消,則會回傳 |
| IsDone |
如果完成此任務,則返回 |
| JniIdentityHashCode |
取得由互通執行時指派給此 Java 對等端的身份雜湊碼。 (繼承來源 Object) |
| JniManagedPeerState |
|
| JniPeerMembers |
|
| PeerReference |
取得這個 Java 節點的 JNI 物件參考。 (繼承來源 Object) |
| PendingCount |
回傳目前待處理的計數。 -或- 將待處理計數設定為給定值。 |
| RawRawResult |
回傳由 回 Join()傳的結果,即使該任務異常完成,或 |
| RawResult |
回傳計算結果。 |
| Root |
回傳目前計算的根;i. |
| ThresholdClass |
|
| ThresholdType |
|
方法
| 名稱 | Description |
|---|---|
| AddToPendingCount(Int32) |
將給定值(原子層次)加到待處理的計數中。 |
| Cancel(Boolean) |
試圖取消執行此任務。 (繼承來源 ForkJoinTask) |
| Clone() |
建立並回傳此物件的副本。 (繼承來源 Object) |
| CompareAndSetForkJoinTaskTag(Int16, Int16) |
原子條件設定此任務的標籤值。 (繼承來源 ForkJoinTask) |
| CompareAndSetPendingCount(Int32, Int32) |
僅當待計數目前保持給定期望值時,才能(原子層次)將待計數設定為給定計數。 |
| Complete(Object) |
無論有沒有待處理計數,呼叫 |
| CompleteExceptionally(Throwable) |
會異常完成此任務,若未中止或取消,則會對相關操作拋出該異常 |
| Compute() |
此任務主要執行計算。 |
| Construct(JniObjectReference, JniObjectReferenceOptions) |
|
| DecrementPendingCountUnlessZero() |
若待處理計數非零,則(原子層面)會遞減。 |
| Dispose() |
釋放該 Java 節點所持有的資源。 (繼承來源 Object) |
| Dispose(Boolean) |
釋放該 Java 節點所持有的資源。 (繼承來源 Object) |
| DisposeUnlessReferenced() |
|
| Equals(Object) |
|
| Equals(Object) |
表示是否有其他物體「等同」於此物。 (繼承來源 Object) |
| ExceptionNow() |
|
| Exec() |
實作 CountedCompletionrs 的執行慣例。 |
| 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) |
| QuietlyJoin(Int64, TimeUnit) |
嘗試加入此任務,若在逾時前完成(可能異常)且當前執行緒未被中斷,則回傳 true。 (繼承來源 ForkJoinTask) |
| QuietlyJoinUninterruptibly(Int64, TimeUnit) |
嘗試加入此任務,若在給定的超時前完成(可能異常完成),則返回為真。 (繼承來源 ForkJoinTask) |
| Reinitialize() |
重置此任務的內部記帳狀態,允許後續 |
| ResultNow() |
|
| SetForkJoinTaskTag(Int16) |
原子式設定此任務的標籤值並回傳舊值。 (繼承來源 ForkJoinTask) |
| SetHandle(IntPtr, JniHandleOwnership) |
設定 Handle 屬性。 (繼承來源 Object) |
| SetPeerReference(JniObjectReference, JniObjectReferenceOptions) |
|
| SetRawResult(Object) |
結果產生的 CountedCompleters 可選擇性地使用此方法來協助維護結果資料。 |
| State() |
|
| ToArray<T>() |
從這個 Java 陣列包裝器建立一個受管理陣列。 (繼承來源 Object) |
| ToString() |
回傳物件的字串表示。 (繼承來源 Object) |
| TryComplete() |
若待處理計數非零,則計數遞減;否則 呼叫 |
| TryUnfork() |
嘗試取消排程執行此任務。 (繼承來源 ForkJoinTask) |
| UnregisterFromRuntime() |
將此 Java 節點從互通執行時中取消註冊。 (繼承來源 Object) |
| Wait() |
導致目前執行緒等待被喚醒,通常是透過 <em>通知</><em 或 em>中斷</em> 來喚醒。 (繼承來源 Object) |
| Wait(Int64, Int32) |
會讓目前執行緒等待喚醒,通常是透過 <em>通知</><em 或 em>中斷</em>,或是經過一定的真實時間。 (繼承來源 Object) |
| Wait(Int64) |
會讓目前執行緒等待喚醒,通常是透過 <em>通知</><em 或 em>中斷</em>,或是經過一定的真實時間。 (繼承來源 Object) |
明確介面實作
| 名稱 | Description |
|---|---|
| IJavaPeerable.Disposed() |
|
| IJavaPeerable.Finalized() |
|
| IJavaPeerable.JniObjectReferenceControlBlock |
|
| IJavaPeerable.SetJniIdentityHashCode(Int32) |
|
| IJavaPeerable.SetJniManagedPeerState(JniManagedPeerStates) |
|
| IJavaPeerable.SetPeerReference(JniObjectReference) |
|
| IJavaPeerable.UnregisterFromRuntime() |
|
擴充方法
| 名稱 | Description |
|---|---|
| GetAsync(IFuture, Int64, TimeUnit) |
|
| GetAsync(IFuture) |
|
| GetJniTypeName(IJavaPeerable) |
取得實例 |
| JavaAs<TResult>(IJavaPeerable) |
試著強制 |
| JavaCast<TResult>(IJavaObject) |
執行 Android 執行時檢查型別轉換。 |
| JavaCast<TResult>(IJavaObject) |
|
| TryJavaCast<TResult>(IJavaPeerable, TResult) |
試著強制 |