Как завершить задачу и продолжить следующую по истечении заданного срока? [Дубликат]

15
задан Hooked 6 March 2015 в 05:44
поделиться

7 ответов

В самом деле, вы не можете ... Единственный способ сделать это - либо использовать thread.stop, либо согласиться на «совместный» метод (например, периодически проверять Thread.isInterrupted или вызывать метод, который генерирует InterruptedException, например Thread.sleep ()) или каким-то образом полностью вызывать метод в другой JVM.

Для определенных видов тестов вызов stop () в порядке, но это, вероятно, повредит состояние вашего тестового набора, поэтому вам придется перезапустить JVM после каждого вызова, чтобы остановить (), если вы чтобы избежать эффектов взаимодействия.

Для хорошего описания того, как реализовать кооперативный подход, ознакомьтесь с частотами Sun по устаревшим методам Thread .

Для пример этого подхода в реальной жизни, Объект Eclipse RCP Job API «IProgressMonitor» позволяет некоторым службам управления сигнализировать подпроцессы (через метод «cancel»), которые они должны останавливать. Конечно, это зависит от методов фактического контроля метода isCancelled, который они часто не выполняют.

Гибридный подход может состоять в том, чтобы спросить поток красиво с прерыванием, а затем настаивать через пару секунд с остановкой. Опять же, вы не должны использовать stop в производственном коде, но в этом случае это может быть хорошо, особенно. если вы выйдете из JVM вскоре после этого.

Чтобы проверить этот подход, я написал простую упряжь, которая выполняет runnable и пытается ее выполнить. Не могу комментировать / редактировать.

public void testStop(Runnable r) {
    Thread t = new Thread(r);
    t.start();
    try {
        t.join(2000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }

    if (!t.isAlive()) {
        System.err.println("Finished on time.");
        return;
    }

    try {
        t.interrupt();
        t.join(2000);
        if (!t.isAlive()) {
            System.err.println("cooperative stop");
            return;
        }
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    System.err.println("non-cooperative stop");
    StackTraceElement[] trace = Thread.getAllStackTraces().get(t);
    if (null != trace) {
        Throwable temp = new Throwable();
        temp.setStackTrace(trace);
        temp.printStackTrace();
    }
    t.stop();
    System.err.println("stopped non-cooperative thread");
}

Чтобы проверить это, я написал два конкурирующих бесконечных цикла, один кооператив и один, который никогда не проверяет прерывистый бит потока.

public void cooperative() {
    try {
        for (;;) {
            Thread.sleep(500);
        }
    } catch (InterruptedException e) {
        System.err.println("cooperative() interrupted");
    } finally {
        System.err.println("cooperative() finally");
    }
}

public void noncooperative() {
    try {
        for (;;) {
            Thread.yield();
        }
    } finally {
        System.err.println("noncooperative() finally");
    }
}

Наконец, я написал тесты (JUnit 4), чтобы их реализовать:

@Test
public void testStopCooperative() {
    testStop(new Runnable() {
        @Override
        public void run() {
            cooperative();
        }
    });
}

@Test
public void testStopNoncooperative() {
    testStop(new Runnable() {
        @Override
        public void run() {
            noncooperative();
        }
    });
}

Раньше я никогда не использовал Thread.stop (), поэтому я не знал о его работе. Он работает, бросая объект ThreadDeath из того, что целевой поток в настоящее время запущен. Это расширяет Ошибка. Таким образом, хотя он не всегда работает чисто, он обычно оставляет простые программы с довольно разумным состоянием программы. Например, вызываются все окончательные блоки. Если вы хотели быть настоящим рывком, вы могли бы уловить ThreadDeath (или Error) и продолжать работать в любом случае!

Если ничего другого, это действительно заставляет меня пожелать большего кода подход IProgressMonitor - добавление другого параметра к методам, которые может занять некоторое время, и побудить разработчика метода изредка опросить объект Monitor, чтобы узнать, хочет ли пользователь отказаться от системы. Я попытаюсь следовать этой схеме в будущем, особенно методы, которые могут быть интерактивными. Конечно, вы не обязательно заранее знаете, какие методы будут использоваться таким образом, но это то, на что, по-видимому, являются Profilers.

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

3
ответ дан 2 revs 19 August 2018 в 12:50
поделиться

Вы должны взглянуть на эти классы: FutureTask , Callable , Исполнители

Вот пример:

public class TimeoutExample {
    public static Object myMethod() {
        // does your thing and taking a long time to execute
        return someResult;
    }

    public static void main(final String[] args) {
        Callable<Object> callable = new Callable<Object>() {
            public Object call() throws Exception {
                return myMethod();
            }
        };
        ExecutorService executorService = Executors.newCachedThreadPool();

        Future<Object> task = executorService.submit(callable);
        try {
            // ok, wait for 30 seconds max
            Object result = task.get(30, TimeUnit.SECONDS);
            System.out.println("Finished with result: " + result);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (TimeoutException e) {
            System.out.println("timeout...");
        } catch (InterruptedException e) {
            System.out.println("interrupted");
        }
    }
}
11
ответ дан Alex 19 August 2018 в 12:50
поделиться
  • 1
    Единственное, что я хотел бы добавить, это finally { executorService .shutdown(); } выполнить некоторую очистку. – Greg 24 March 2016 в 20:43

Механизм прерывания Java предназначен для такого сценария. Если метод, который вы хотите прервать, выполняет цикл, просто попросите его проверить состояние прерывания потока на каждой итерации. Если это прерывается, вызовите InterruptedException.

Затем, когда вы хотите прервать, вам просто нужно вызвать прерывание в соответствующем потоке.

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

8
ответ дан Dan Dyer 19 August 2018 в 12:50
поделиться

Правильный ответ - это, я считаю, создание Runnable для выполнения подпрограммы и запуск этого в отдельном потоке. Runnable может быть FutureTask, который можно запустить с помощью тайм-аута (метод get). Если он истечет, вы получите исключение TimeoutException, в котором я предлагаю вам

  • вызывать thread.interrupt (), чтобы попытаться закончить его полукооперативным способом (многие вызовы библиотеки кажутся быть чувствительным к этому, поэтому он, вероятно, будет работать)
  • немного подождать (Thread.sleep (300))
  • , а затем, если поток все еще активен (thread.isActive ( )), вызовите thread.stop (). Это устаревший метод, но, по-видимому, единственная игра в городе, которая не запускает отдельный процесс со всем, что это влечет за собой.

В моем приложении, где я запускаю ненадежный, неконтактный код, написанный моим начинающих учеников, я делаю выше, гарантируя, что убитый поток никогда не имеет (не пишет) доступа к любым объектам, которые пережили его смерть. Сюда входит объект, в котором размещается вызываемый метод, который отбрасывается, если происходит таймаут. (Я говорю своим ученикам, чтобы избежать тайм-аутов, потому что их агент будет дисквалифицирован.) Я не уверен в утечке памяти ...

Я различаю длительные промежутки времени (метод завершает) и жесткие таймауты - жесткие таймауты

Из моих исследований, Java, похоже, не имеет устаревшего положения для запуска несовместимого кода, что в некотором смысле является зияющей дырой в модели безопасности. Либо я могу запускать внешний код и управлять правами, которые он имеет (SecurityManager), либо я не могу запускать внешний код, потому что он может в конечном итоге заняться целым ЦП без каких-либо устаревших средств для его остановки.

double x = 2.0;  
while(true) {x = x*x}; // do not terminate
System.out.print(x); // prevent optimization
0
ответ дан davidswelt 19 August 2018 в 12:50
поделиться

Никто не ответил на него напрямую, так что вот ближайшая вещь, которую я могу дать вам в коротком количестве кода psuedo:

обернуть метод в runnable / callable. Сам метод должен будет проверить прерванный статус, если вы хотите его остановить (например, если этот метод является циклом, внутри проверки цикла для Thread.currentThread () isInterrupted, и если это так, остановите цикл (дон Однако не проверяйте каждую итерацию, или вы просто замедляете работу. В методе обтекания используйте thread.join (timeout), чтобы подождать время, в течение которого вы хотите запустить этот метод, или внутри цикла туда соедините соединение несколько раз с меньшим таймаутом, если вам нужно делать другие вещи во время ожидания. Если метод не завершит, после присоединения, используйте приведенные выше рекомендации для прерывания быстрой / чистой.

, поэтому код мудрый, старый код:

void myMethod()
{
    methodTakingAllTheTime();
}

новый код:

void myMethod()
{
    Thread t = new Thread(new Runnable()
        {
            public void run()
            {
                 methodTakingAllTheTime(); // modify the internals of this method to check for interruption
            }
        });
    t.join(5000); // 5 seconds
    t.interrupt();
}

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

1
ответ дан John Gardner 19 August 2018 в 12:50
поделиться
  • 1
    На самом деле, вы не достигнете t.interrupt (). Thread.join выполнит прерванное исключение, и t.interrupt будет обходить. Фактически, поскольку InterruptedException проверяется, вышесказанное не будет компилироваться. Хммм ... Я должен проверить себя, прежде чем разрушу себя ... – krakatoa 27 October 2008 в 21:42
  • 2
    Я ошибался в том, что thread.join использует interruptedException. Это будет продолжаться. Таким образом, ваш код правильный, по модулю пропущенного объявления throw. – krakatoa 27 October 2008 в 22:03

Я предполагаю использование нескольких потоков в следующих операторах.

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

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

7
ответ дан Tim Stewart 19 August 2018 в 12:50
поделиться
  • 1
    спасибо за Ваш ответ. в этом случае функция не может быть спроектирована для периодической проверки примитива переменной или синхронизации. – carrier 27 October 2008 в 18:43

Я могу придумать не очень хороший способ сделать это. Если вы можете обнаружить, когда он занимает слишком много времени, вы можете проверить метод на логическом уровне на каждом шаге. Попросите программу изменить значение boolean tooMuchTime на true, если оно занимает слишком много времени (я не могу с этим поделать). Затем используйте что-то вроде этого:

 Method(){
 //task1
if (tooMuchTime == true) return;
 //task2
if (tooMuchTime == true) return;
 //task3
if (tooMuchTime == true) return;
//task4
if (tooMuchTime == true) return;
//task5
if (tooMuchTime == true) return;
//final task
  }
0
ответ дан WVrock 19 August 2018 в 12:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: