Как я могу сообщить о прогрессе от фоновой задачи? - PullRequest
4 голосов
/ 05 января 2010

У меня есть долго выполняющаяся задача, которая выполняется в фоновом режиме в пуле потоков ExecutorService. Каковы некоторые лучшие практики с точки зрения этой задачи, возвращающие прогресс или промежуточные результаты? Есть ли библиотеки, которые предоставляют эту функциональность?

РЕДАКТИРОВАТЬ: Чтобы уточнить, я говорю о сообщении прогресса в другой код, а не пользователю.

Обычно я бы использовал SwingWorker, но я работаю с бэкэндом Java / Groovy для приложения Grails, и я не уверен, как это будет вести себя в среде безголового сервера, поскольку он имеет связи EDT.

Другим примером является среда Jobs в Eclipse RCP, но мне нужно что-то, что не связано с пользовательским интерфейсом.

Ответы [ 4 ]

3 голосов
/ 05 января 2010

Эй, вы могли бы попытаться реализовать Шаблон наблюдателя и попросить заинтересованные стороны подписаться на рабочий поток (расширение java.util.Observable или аналогичный) или другой класс, который управляет наблюдателями.

Вы можете использовать java.util.Observer и java.util.Observable или сверните свое собственное.

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

public interface ObservableSubject<T extends SubjectObserver, V> {

   void registerObserver(T observer);

   void removeObserver(T observer);

   void notifyObservers(V notificationPayload); 

}


public interface SubjectObserver<T> {

   void handleNotification(T notificationPayload);
}

Дополнительная информация: Шаблон наблюдателя в Википедии

1 голос
/ 05 января 2010

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

0 голосов
/ 23 мая 2017

Я разработал простой интерфейс для этого:

public interface Process<TState, TResult> {

    void onProgress(final Consumer<TState> callback);

    void onCompletion(final Consumer<TResult> callback);
}

Использование выглядит так:

final Process<Float, Either<IOException, String>> p = download(executor, url);

p.onProgress(progress -> {
    System.out.println("Progress: " + progress * 100);
});

p.onComplete(result -> {
    System.out.println("Finished: " + result.toString());
});

И общая реализация, которая должна быть поточно-ориентированной:

import com.google.common.base.Preconditions;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public final class SettableProcess<TState, TResult> implements Process<TState, TResult> {

    private final Object LOCK = new Object();

    private final List<Consumer<TState>> progressCallbacks;
    private final List<Consumer<TResult>> completionCallbacks;

    private volatile boolean isComplete;
    private volatile TResult result;

    private SettableProcess() {

        progressCallbacks = new ArrayList<>();
        completionCallbacks = new ArrayList<>();

        isComplete = false;
        result = null;
    }

    @Override
    public void onProgress(final Consumer<TState> callback) {
        Preconditions.checkNotNull(callback);
        if (!isComplete) {
            synchronized (LOCK) {
                if (!isComplete) {
                    progressCallbacks.add(callback);
                }
            }
        }
    }

    @Override
    public void onCompletion(final Consumer<TResult> callback) {
        Preconditions.checkNotNull(callback);
        synchronized (LOCK) {
            if (isComplete) {
                callback.accept(result);
            } else {
                completionCallbacks.add(callback);
            }
        }
    }

    public void complete(final TResult result) {
        Preconditions.checkNotNull(result);
        Preconditions.checkState(!isComplete);
        synchronized (LOCK) {
            Preconditions.checkState(!isComplete);
            this.isComplete = true;
            this.result = result;
            for (final Consumer<TResult> callback : completionCallbacks) {
                callback.accept(result);
            }
        }
        completionCallbacks.clear();
        progressCallbacks.clear();
    }

    public void progress(final TState state) {
        Preconditions.checkNotNull(state);
        Preconditions.checkState(!isComplete);
        synchronized (LOCK) {
            Preconditions.checkState(!isComplete);
            for (final Consumer<TState> callback : progressCallbacks) {
                callback.accept(state);
            }
        }
    }

    public static <TState, TResult> SettableProcess<TState, TResult> of() {
        return new SettableProcess<>();
    }
}

Это может быть расширено для поддержки отмены и так далее.

0 голосов
/ 05 января 2010

Ответы от Адриана и edwardTheGreat - оба хороших варианта. Все зависит от того, как вы хотите, чтобы «другой код» потреблял обновления статуса. Третий вариант - использовать очередь сообщений, в которую фоновый поток записывает периодическое состояние. Действительно общая версия этого будет использовать JMS.

...