CountedCompleter Класс
Определение
Важно!
Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
Действие 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), за исключением того, что для активации действия #onCompletion(CountedCompleter)завершения может потребоваться несколько <>завершений<> em, а не только один. Если не инициализировано в противном случае, число ожидающих #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. Это возможно, но редко применимо, чтобы переопределить этот метод для поддержания других объектов или полей, содержащих данные результатов.
CountedCompleter, у которого нет полного (т. е., для которого #getCompleter возвращается null), можно использовать в качестве обычной функции ForkJoinTask с этой добавленной функциональностью. Однако любой полный, который, в свою очередь, имеет еще один полный служит только внутренним вспомогательным средством для других вычислений, поэтому его собственное состояние задачи (как сообщается в таких методахForkJoinTask#isDone), является произвольным; это состояние изменяется только при явных вызовах #complete, ForkJoinTask#cancelForkJoinTask#completeExceptionally(Throwable) или при исключительном завершении методаcompute. При любом исключительном завершении исключение может быть передано в завершенный объект задачи (и его завершение и т. д.), если он существует, и он еще не завершен. Аналогичным образом, отмена внутреннего countedCompleter имеет только локальное влияние на этот полный, поэтому не часто полезно.
<b>Примеры использования.</b>
<b>Параллельная рекурсивная декомпозиция.</b> CountedCompleters может быть организовано в деревьях, аналогичных тем, которые часто используются с RecursiveActions, хотя конструкции, участвующие в настройке их, как правило, различаются. Здесь завершение каждой задачи является родительским в дереве вычислений. Несмотря на то, что они влечет за собой немного больше бухгалтерии, CountedCompleters может быть лучшим вариантом при применении возможной многократной операции (которая не может быть дополнительно разделена) к каждому элементу массива или коллекции; особенно если операция занимает значительно разное время для некоторых элементов, чем другие, либо из-за встроенных вариантов (например, ввода-вывода) или вспомогательных эффектов, таких как сборка мусора. Так как CountedCompleters предоставляют свои собственные продолжения, другие задачи не должны блокировать ожидание их выполнения.
Например, ниже приведена начальная версия метода служебной программы, использующего рекурсивное разбиение на два рекурсивного фрагмента для разделения работы на отдельные части (конечные задачи). Даже если работа разделена на отдельные вызовы, методы на основе деревьев обычно предпочтительнее напрямую килировать конечные задачи, так как они снижают взаимодействие между потоками и улучшают балансировку нагрузки. В рекурсивном случае второй из каждой пары подзадач, чтобы завершить завершение триггеров родительского элемента (так как сочетание результатов не выполняется, реализация метода 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();
}}
Эта конструкция может быть улучшена, заметив, что в рекурсивном случае задача не имеет ничего общего после вилки правой задачи, поэтому можно напрямую вызвать ее левую задачу перед возвратом. (Это аналог удаления рекурсии хвоста.) Кроме того, когда последнее действие в задаче заключается в том, чтобы вилировать или вызвать подзадачку ("хвостовый вызов"), вызов, который 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). Как показано в следующем классе (который выполняет упрощенную форму map-reduce, где сопоставления и сокращения являются всеми типами 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 создавать настраиваемые обходы. Например, чтобы определить 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 для java.util.concurrent.CountedCompleter.
Части этой страницы — это изменения на основе работы, созданной и общей проектом с открытым исходным кодом Android и используемой в соответствии с условиями, описанными в лицензии Creative Commons 2.5 Attribution.
Конструкторы
| 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>. (Унаследовано от Object) |
| Wait(Int64) |
Приводит к тому, что текущий поток будет ждать, пока он не проснется, как правило, при <>получении уведомления</>em или <>эм прервано< или> до тех пор, пока не истекло определенное количество реального времени. (Унаследовано от Object) |
| Wait(Int64, Int32) |
Приводит к тому, что текущий поток будет ждать, пока он не проснется, как правило, при <>получении уведомления</>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) |
Действие |