Завершить долгосрочную задачу в фоновом потоке - PullRequest
2 голосов
/ 10 октября 2019

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

private void writeCompressedData() {
    int i, j, r, c, a, b;
    loat[][] inputArray;

    for (r = 0; r < minBlockHeight; r++) {
        for (c = 0; c < minBlockWidth; c++) {
            xpos = c * 8;
            pos = r * 8;
            for (comp = 0; comp < jpegObj.numberOfComponents; comp++) {
                inputArray = (float[][]) jpegObj.components[comp];

                 for (i = 0; i < jpegObj.VsampFactor[comp]; i++) {
                     for (j = 0; j < jpegObj.HsampFactor[comp]; j++) {
                         xblockoffset = j * 8;
                         yblockoffset = i * 8;
                         for (a = 0; a < 8; a++) {
                             for (b = 0; b < 8; b++) {
                                 // Process some data and put to inputArray
                             }
                         }

                         // Encode Huffman block 
                     }
                 }
             }
         }
     }
}

Я запускаю этот метод в нормальном потоке, например:

new Thread(new Runnable() {
    @Override
    public void run() {
        writeCompressedData();
    }
});

Илизапустить в фоновом рабочем потоке

TaskExecutor.queueRunnable(new Runnable() {
    @Override
    public void run() {
        writeCompressedData();
    }
});

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

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


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

Ответы [ 4 ]

2 голосов
/ 11 октября 2019

Вам потребуется некоторая форма совместного отмены, скажем, проверка Thread.currentThread().isInterrupted() внутри одного или нескольких вложенных циклов.

for (/* ... */) {
    if (Thread.currentThread().isInterrupted()) return;

    for (/* ... */) {

        if (Thread.currentThread().isInterrupted()) return;

        for (/* ... */) {
            // the tightest loop
        }             
    }
}

Затем, когда вы запустите метод, сохраните Thread / Future и позвоните interrupt / cancel(true):

backgroundTask = new Thread(() -> method());
backgroundTask.start();
// ...
backgroundTask.interrupt();

backgroundFuture = executorService.submit(() -> method());
// ...
backgroundFuture.cancel(true);

В RxJava это будет выглядеть примерно так:

backgroundDisposable = Completable.fromAction(() -> method())
.subscribeOn(Schedulers.io()) // dedicated thread recommended
.observeOn(AndroidSchedulers.mainThread())
.subscribe(() -> { /* done */ }, e -> { /* error */ });

// ...
backgroundDisposable.dispose();
1 голос
/ 10 октября 2019

Вы можете использовать Java ExecutorService с таймаутом и Future, чтобы решить эту проблему. Посмотреть это сообщение .

0 голосов
/ 24 октября 2019

Мой подход с использованием RxJava и, более конкретно, с использованием Timeout оператора RxJava, Вот суть кода, Через 5 секунд будет вызываться onError, как тамне излучение элемента в течение предыдущих 5 секунд,

private void timeOutObserver() {

        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) {
                emitter.onNext("A");
            }
        })
                .timeout(5000, TimeUnit.MILLISECONDS)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<String>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        Log.d("-@-", "subscribed");
                    }

                    @Override
                    public void onNext(String s) {
                        Log.d("-@-", "on next " + s);
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.d("-@-", e.getLocalizedMessage());
                    }

                    @Override
                    public void onComplete() {
                        Log.d("-@-", "on complete");
                    }
                });
    }

Подробнее о работе с оператором TimeOut см. this ,

0 голосов
/ 10 октября 2019

Лучший способ - запустить такой код в другом процессе. Даже процесс потерпел крах, основной процесс не будет затронут.

...