Получение вывода потока

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

Вы можете опрашивать каждые X миллисекунд для какой-либо общедоступной переменной, называемой «задание завершено» или чего-то еще, кстати, но тогда вы получите результаты позже, чем когда они будут доступны ... основной код будет терять время, ожидая их. С другой стороны, если вы используете меньшее значение X, ЦП будет тратить зря на опрос столько раз.

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

Извините, если он похож на этот другой вопрос, я полагаю, это, вероятно, причина ответа Эбен. Я имел в виду запускать множество потоков и знать, когда все они завершились, без их опроса.

Я больше думал о том, чтобы разделить нагрузку на ЦП между несколькими ЦП с использованием пакетов потоков и узнать, когда пакет завершился. Я полагаю, что это можно сделать с объектами Будущееs, но этот метод блокировки получить очень похож на скрытую блокировку, а не на то, что мне нравится.

Спасибо всем за вашу поддержку. Хотя мне также понравился ответ Эриксон, я думаю, что сауа является наиболее полным и тот, который я буду использовать в своем собственном коде.

> и знать, когда закончилась партия. Почему вы хотите это знать? Вам что-то нужно из этой партии? Или вы хотите знать, когда вы можете запланировать больше задач?

Joachim Sauer 09.12.2008 18:25

Это очень похоже на этот вопрос.

kgiannakakis 09.12.2008 16:14
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
12
2
12 631
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Ответ принят как подходящий

Не используйте низкоуровневые конструкции, такие как потоки, если вам абсолютно не нужны мощность и гибкость.

Вы можете использовать ExecutorService, например ThreadPoolExecutor to представить()Callables. Это вернет объект Будущее.

Используя этот объект Future, вы можете легко проверить, выполнено ли это, и получить результат (включая блокирующий get(), если это еще не сделано).

Эти конструкции значительно упростят наиболее распространенные многопоточные операции.

Хочу уточнить про блокировку get():

Идея состоит в том, что вы хотите запустить некоторые задачи (Callable), которые выполняют некоторую работу (расчет, доступ к ресурсам, ...), где вам не нужен результат прямо сейчас. Вы можете просто положиться на Executor, чтобы запускать ваш код в любое время (если это ThreadPoolExecutor, он будет запускаться всякий раз, когда будет доступен бесплатный поток). Затем в какой-то момент вы, вероятно, необходимость результат вычисления продолжите. На этом этапе вы должны вызвать в get(). Если задача уже была запущена в этот момент, get() сразу же вернет значение. Если задача не завершилась, то вызов get() будет ждать, пока задача не будет завершена. Обычно это желательно, поскольку вы все равно не можете продолжить работу без результата задачи.

Если вам не нужно значение для продолжения, но вы хотели бы узнать о нем, если оно уже доступно (возможно, чтобы показать что-то в пользовательском интерфейсе), вы можете легко вызвать isDone() и вызвать get() только в том случае, если это возвращает true).

Опрос или занятое ожидание - не лучшая идея. Как вы упомянули, активное ожидание тратит впустую циклы ЦП и может привести к тому, что ваше приложение перестает отвечать на запросы.

Моя Java грубая, но вам нужно что-то вроде следующего:

Если один поток должен дождаться вывода другого потока, вы должны использовать переменную условия.

final Lock lock = new ReentrantLock();
final Condition cv = lock.newCondition();

Поток, заинтересованный в выводе другой угрозы, должен вызвать в cv.wait(). Это приведет к блокировке текущего потока. Когда рабочий поток завершает за работой, он должен вызвать cv.signal(). Это приведет к тому, что заблокированный поток станет разблокированным, что позволит ему проверить вывод рабочего потока.

Как отмечает saua: используйте конструкции, предлагаемые java.util.concurrent. Если вы застряли с JRE до 1.5 (или 5.0), вы можете прибегнуть к собственному скатыванию, но все же лучше использовать backport: http://backport-jsr166.sourceforge.net/

В качестве альтернативы API параллелизма, как описано Saua (и если основной поток не должен знать, когда рабочий поток завершается), вы можете использовать шаблон публикации / подписки.

В этом сценарии дочернему Thread / Runnable дается слушатель, который знает, как обрабатывать результат и который вызывается, когда дочерний Thread / Runnable завершает работу.

Ваш сценарий все еще немного неясен.

Если вы выполняете пакетное задание, вы можете использовать invokeAll. Это заблокирует ваш основной поток, пока все задачи не будут выполнены. В этом подходе нет «занятого ожидания», когда основной поток тратит впустую ЦП на опрос метода isDone Future. Хотя этот метод возвращает список Futures, они уже "готовы". (Существует также перегруженная версия, которая может истекать по таймауту перед завершением, что может быть безопаснее для использования с некоторыми задачами.) Это может быть намного чище, чем пытаться самостоятельно собрать кучу объектов Future и пытаться проверить их статус или заблокировать их. get методы индивидуально.

Если это интерактивное приложение, в котором задачи время от времени выделяются для выполнения в фоновом режиме, использование Перезвони, предложенное Nick.holt, является отличным подходом. Здесь вы используете submit и Runnable. Метод run вызывает обратный вызов с результатом, когда он был вычислен. При таком подходе вы можете отказаться от Future, возвращенного submit, если только вы не хотите иметь возможность запускать задачи cancel без выключения всего ExecutorService.

Если вы хотите иметь возможность отменять задачи или использовать возможности тайм-аута, важно помнить, что задачи отменяются вызовом interrupt в их потоке. Итак, ваша задача должна периодически проверять свой прерванный статус и при необходимости прерывать выполнение.

Подкласс Thread и дайте вашему классу метод, который возвращает результат. Когда вызывается метод, если результат еще не был создан, тогда join () с Thread. Когда join () вернется, работа вашего потока будет завершена, и результат должен быть доступен; верни это.

Используйте это только в том случае, если вам действительно нужно запустить асинхронное действие, поработать во время ожидания, а затем получить результат. В противном случае, в чем смысл потока? С таким же успехом вы можете просто написать класс, который выполняет работу и возвращает результат в основном потоке.

Другой подход - это обратный вызов: пусть ваш конструктор принимает аргумент, реализующий интерфейс с методом обратного вызова, который будет вызываться при вычислении результата. Это сделает работу полностью асинхронной. Но если вам вообще нужно дождаться результата в какой-то момент, я думаю, вам все равно придется вызывать join () из основного потока.

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

Таким образом, вам вообще не нужно будет опрашивать.

Вот пример интерфейса:

/**
 * Listener interface to implement to be called when work has
 * finished.
 */
public interface WorkerListener {
    public void workDone(WorkerThread thread);
}

Вот пример фактического потока, который выполняет некоторую работу и уведомляет своих слушателей:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * Thread to perform work
 */
public class WorkerThread implements Runnable {
    private List listeners = new ArrayList();
    private List results;

    public void run() {
        // Do some long running work here

        try {
            // Sleep to simulate long running task
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        results = new ArrayList();
        results.add("Result 1");

        // Work done, notify listeners
        notifyListeners();
    }

    private void notifyListeners() {
        for (Iterator iter = listeners.iterator(); iter.hasNext();) {
            WorkerListener listener = (WorkerListener) iter.next();
            listener.workDone(this);
        }
    }

    public void registerWorkerListener(WorkerListener listener) {
        listeners.add(listener);
    }

    public List getResults() {
        return results;
    }
}

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

import java.util.Iterator;
import java.util.List;

/**
 * Class to simulate a main program
 */
public class MainProg {
    public MainProg() {
        WorkerThread worker = new WorkerThread();
        // Register anonymous listener class
        worker.registerWorkerListener(new WorkerListener() {
            public void workDone(WorkerThread thread) {
                System.out.println("Work done");
                List results = thread.getResults();
                for (Iterator iter = results.iterator(); iter.hasNext();) {
                    String result = (String) iter.next();
                    System.out.println(result);
                }
            }
        });

        // Start the worker thread
        Thread thread = new Thread(worker);
        thread.start();

        System.out.println("Main program started");
    }

    public static void main(String[] args) {
        MainProg prog = new MainProg();
    }
}

Другие вопросы по теме