Подтвердить что ты не робот

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

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

Вы можете опросить каждые X миллисекунды для какой-либо публичной переменной под названием "job finished" или что-то кстати, но затем вы получите результаты позже, чем когда они будут доступны... основной код будет терять время ожидания для них. С другой стороны, если вы используете более низкий X, CPU будет тратить столько времени на опрос.

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

Я хотел бы уточнить о блокировке get():

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

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

Ответ 2

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

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

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

/**
 * 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();
    }
}

Ответ 3

Опрос a.k.a ожидание - это не очень хорошая идея. Как вы уже сказали, занятый ожидание отнимает процессорные циклы и может привести к тому, что ваше приложение окажется невосприимчивым.

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

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

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

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

Ответ 4

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

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

Ответ 5

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

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

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

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

Ответ 6

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

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

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

Ответ 7

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