Как немедленно остановить задачу, которая запускается с помощью ExecutorService? - PullRequest
2 голосов
/ 26 декабря 2011

Я пробовал много разных способов немедленно остановить задачу, которая запускается с использованием ExecutorService, но безуспешно.

Future<Void> future = executorService.submit(new Callable<Void>(
    public Void call () {
        ... do many other things here..
        if(Thread.currentThread.isInterrupted()) {
            return null;
        }
        ... do many other things here..
        if(Thread.currentThread.isInterrupted()) {
            return null;
        }
    }
));

if(flag) { // may be true and directly cancel the task
    future.cancel(true);
}

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

Проблема в том, что после вызова future.cancel (true) задача не останавливается и Thread.currentThread.isInterrupted () по-прежнему возвращает false и у меня нет никакого способа узнать, что задача была остановлена ​​изнутри метода call ().

Я думаю об установке флага типа cancelled = true после вызова future.cancel (true)и постоянно проверяя этот флаг в методе call (), я думаю, что это хак, и код может быть очень уродливым, потому что пользователь может запустить много задач одновременно.

Есть ли более элегантный способ достижения того, чего я хочу?

РЕДАКТИРОВАТЬ:

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

Я делаю следующее, чтобы запустить 5 задач, каждая задача запускает 5 потоков для загрузки файла.а затем я прекращаю все 5 задач немедленно .Для всех приведенных ниже вызовов методов я запускаю поток (ExecutorService.submit (task)), чтобы сделать его асинхронным, как вы можете судить по суффиксам методов.

int t1 = startTaskAysnc(task1);
int t2 = startTaskAysnc(task2);
int t3 = startTaskAysnc(task3);
int t4 = startTaskAysnc(task4);
int t5 = startTaskAysnc(task5);

int stopTaskAysnc(t1);
int stopTaskAysnc(t2);
int stopTaskAysnc(t3);
int stopTaskAysnc(t4);
int stopTaskAysnc(t5);

в startTaskAysnc () , я просто инициирую сокет-соединение с удаленным сервером, чтобы получить размер файла (и это, безусловно, займет некоторое время), после успешного получения fileSize, я запусту 5 потоков, чтобы загрузить разные части файла,как показано ниже (код упрощен для упрощения отслеживания):

public void startTaskAsync(DownloadTask task) { 
    Future<Void> future = executorService.submit(new Callable<Void>(
        public Void call () {
            // this is a synchronous call
            int fileSize = getFileSize();
            System.out.println(Thread.currentThread.isInterrupted());
            ....
            Future<Void> futures = new Future<Void>[5];
            for (int i = 0; i < futures.length; ++i) {
                futures[i] = executorService.submit(new Callable<Void>(){...});
            }

            for (int i = 0; i < futures.length; ++i) {
                futures[i].get(); // wait for it to complete
            }            
        }
    ));
    synchronized (mTaskMap) {
        mTaskMap.put(task.getId(), future);
    }
}

public void stopTaskAysnc(int taskId) {
    executorService.execute(new Runnable(){
        Future<Void> future = mTaskMap.get(taskId);
        future.cancel(true);
    });
}

Я заметил странное поведение, заключающееся в том, что после вызова stopTaskAsync () для всех 5 задач всегда будет хотя бы одназадача, которая получила остановлена ​​ (то есть Thread.currentThread.isInterrupted () возвращает true), а остальные 4 задачи продолжали выполняться.

И я попробовал ваши предложения, установив UncaughtExceptionHandler, но ничегоИсходя из этого.

РЕДАКТИРОВАТЬ:

Проблема была решена по этой ссылке: Не удается остановить задачу, которая запускается с использованием ExecutorService

Ответы [ 3 ]

4 голосов
/ 26 декабря 2011

Ну, javadoc из Future.cancel(boolean) говорит, что:

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

, поэтому совершенно точно, что поток, выполняющий задачу , прерван .Что могло произойти, так это то, что один из

... делает здесь много других вещей ..

случайно очищает статус Thread interruptedбез выполнения желаемой обработки.Если вы установите точку останова в Thread.interrupt(), вы можете поймать преступника.

Другой вариант, о котором я могу подумать, это то, что задание завершается до захвата прерывания, либо потому, что оно завершено, либо выдает какое-то необработанное исключение.Звоните Future.get(), чтобы определить это.В любом случае, как упоминал asdasd, хорошей практикой является установка UncaughtExceptionHandler.

2 голосов
/ 26 декабря 2011

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

  • дождитесь окончания загрузки (которую я назову контроллерами)
  • или попросить контроллеры остановить

Это означает, что если число ядер ядра достигнуто после запуска контроллера, загрузчики будут помещены в очередь пула потоков, и поток контроллера никогда не завершится. Аналогичным образом, если при выполнении задачи отмены достигается базовое число потоков, эта задача отмены будет помещена в очередь и не будет выполняться, пока не будет завершена какая-либо другая задача.

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

0 голосов
/ 26 декабря 2011

Я думаю, вы найдете решение здесь .Суть в том, что метод отмены вызывает InterruptedException.Пожалуйста, проверьте, работает ли ваш поток после отмены?Вы уверены, что не пытались прервать законченную тему?Вы уверены, что ваш поток не вышел из строя с любым другим исключением?Попробуйте настроить UncaughtExceptionHandler.

...