Java ForkJoinPool - порядок задач в очередях - PullRequest
0 голосов
/ 20 сентября 2018

Я хотел бы понять порядок, в котором задачи обрабатываются в пуле Java fork-join.

Пока что единственная соответствующая информация, которую я нашел в документации, касается параметра, называемого "asyncMode".", что является" истиной, если этот пул использует локальный режим планирования "первым пришел - первым вышел" для разветвленных задач, которые никогда не объединяются ".

Моя интерпретация этого утверждения заключается в том, что у каждого работника есть собственная очередь задач;работники берут задания с начала своей очереди или крадут за очереди других работников, если их собственные очереди пусты;работники добавляют вновь разветвленные задачи в конец (соответственно фронт) своих собственных очередей, если asyncMode имеет значение true (соответственно, false).

Пожалуйста, исправьте меня, если моя интерпретация неверна!

Сейчасэто вызывает пару вопросов:

1) Каков порядок для разветвленных задач, к которым присоединено ?

Мне кажется, чтокогда задача разветвляется, она добавляется в очередь работника, как описано в моей интерпретации выше.Теперь предположим, что задача присоединена ...

  • Если при вызове объединения задача еще не запущена, рабочий, вызывающий соединение, вытянет задачу из очереди инемедленно приступить к работе над ним.

  • Если при вызове соединения задача уже была украдена другим работником, то рабочий, вызывающий соединение, будет работать над другими задачами в то же времяпорядок получения заданий, описанных в моей интерпретации выше), пока задача, к которой оно присоединяется, не была завершена работником, который его украл.

Это предположение основано на написании простого тестового кодас помощью операторов печати и наблюдения за тем, как изменение порядка вызовов соединения влияет на порядок обработки задач.Может ли кто-нибудь сказать мне, если мои предположения верны?

2) Каков порядок для задач, которые отправляются извне?

Согласно ответ нана этот вопрос пулы с объединением вил не используют внешние очереди.(Я использую Java 8, между прочим.)

Итак, я понимаю, что, когда задача отправляется извне, задача добавляется в случайно выбранную рабочую очередь?

Если да, добавляется ли отправленная извне задача в конец или в начало очереди?

Наконец, зависит ли это от того, передана ли задача путем вызова pool.execute (task) или по вызову pool.invoke (задача)?И зависит ли это от того, является ли поток, вызывающий pool.execute (задача) или pool.invoke (задача), внешним потоком или потоком в этом пуле разветвления?

1 Ответ

0 голосов
/ 05 ноября 2018
  1. Ваше предположение верно, вы абсолютно правы.Как вы можете прочитать в " Обзор реализации ".
 * Joining Tasks
 * =============
 *
 * Any of several actions may be taken when one worker is waiting
 * to join a task stolen (or always held) by another.  Because we
 * are multiplexing many tasks on to a pool of workers, we can't
 * just let them block (as in Thread.join).  We also cannot just
 * reassign the joiner's run-time stack with another and replace
 * it later, which would be a form of "continuation", that even if
 * possible is not necessarily a good idea since we may need both
 * an unblocked task and its continuation to progress.  Instead we
 * combine two tactics:
 *
 *   Helping: Arranging for the joiner to execute some task that it
 *      would be running if the steal had not occurred.
 *
 *   Compensating: Unless there are already enough live threads,
 *      method tryCompensate() may create or re-activate a spare
 *      thread to compensate for blocked joiners until they unblock.

2. Оба ForkJoinPool.invoke и ForkJoinPool.join - это одно и то жев порядке, в котором задание поставлено.Вы можете видеть в коде

    public <T> T invoke(ForkJoinTask<T> task) {
        if (task == null)
            throw new NullPointerException();
        externalPush(task);
        return task.join();
    }
    public void execute(ForkJoinTask<?> task) {
        if (task == null)
            throw new NullPointerException();
        externalPush(task);
    }

В externalPush вы можете видеть, что задача добавляется в случайно выбранную рабочую очередь с помощью ThreadLocalRandom.Более того, он вошел в начало очереди с помощью метода push.

    final void externalPush(ForkJoinTask<?> task) {
        WorkQueue[] ws; WorkQueue q; int m;
        int r = ThreadLocalRandom.getProbe();
        int rs = runState;
        if ((ws = workQueues) != null && (m = (ws.length - 1)) >= 0 &&
            (q = ws[m & r & SQMASK]) != null && r != 0 && rs > 0 &&
            U.compareAndSwapInt(q, QLOCK, 0, 1)) {
            ForkJoinTask<?>[] a; int am, n, s;
            if ((a = q.array) != null &&
                (am = a.length - 1) > (n = (s = q.top) - q.base)) {
                    int j = ((am & s) << ASHIFT) + ABASE;
                U.putOrderedObject(a, j, task);
                U.putOrderedInt(q, QTOP, s + 1);
                U.putIntVolatile(q, QLOCK, 0);
                if (n <= 1)
                    signalWork(ws, q);
                return;
            }
            U.compareAndSwapInt(q, QLOCK, 1, 0);
        }
        externalSubmit(task);
    }

Я не уверен, что вы подразумеваете под этим:

И зависит ли это от того,поток, вызывающий pool.execute (задача) или pool.invoke (задача), является внешним потоком или потоком в этом пуле разветвления?

...