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

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

Я использую Java.

чтобы проиллюстрировать:

logger.info("sequentially executing all batches...");
for (TestExecutor executor : builder.getExecutors()) {
logger.info("executing batch...");
executor.execute();
}

Я считаю, что класс TestExecutor должен implement Callable и продолжить в этом направлении.

Но все, что я хочу сделать, это остановить executor.execute(), если он занимает слишком много времени.

Предложения...

ИЗМЕНИТЬ

Многие из полученных предложений предполагают, что выполняемый метод, который занимает много времени, содержит какой-то цикл и периодически может проверяться переменная. Однако, это не так. Таким образом, что-то, что не обязательно будет чистым, и это просто прекратит выполнение, если оно приемлемо.

Ответы

Ответ 1

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

Вот пример:

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");
        }
    }
}

Ответ 2

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

Для примера такого подхода в реальной жизни API-интерфейс 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, но это потребуется для этого подхода.

Ответ 5

Никто не ответил на него напрямую, так что вот ближайшая вещь, которую я могу дать вам в коротком количестве кода 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 или этот поток будет продолжать работать после того, как вы вызвали прерывание.

Ответ 6

Правильный ответ - это, я считаю, создание 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

Ответ 7

Я могу придумать не очень хороший способ сделать это. Если вы можете обнаружить, когда он занимает слишком много времени, вы можете проверить метод на логическом уровне на каждом шаге. Попросите программу изменить значение 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
  }