Как к тайм-ауту поток

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

Один способ сделать его когда я фигурировал из этого потока, состоит в том, чтобы использовать TimerTask в выполнении () метод Потока.

Есть ли какие-либо лучшие решения для этого?

 
Править: При добавлении щедрости, поскольку мне был нужен более четкий ответ. Код ExecutorService, данный ниже, не решает мою проблему. Почему я должен спать () после выполнения (некоторый код - у меня нет дескриптора по этой части кода)? Если код завершается, и сон () прерван, как это может быть тайм-аутом?

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

246
задан Michael Myers 26 February 2010 в 07:20
поделиться

8 ответов

На самом деле лучше использовать ExecutorService вместо Timer , вот SSCCE :

package com.stackoverflow.q2275443;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class Test {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(new Task());

        try {
            System.out.println("Started..");
            System.out.println(future.get(3, TimeUnit.SECONDS));
            System.out.println("Finished!");
        } catch (TimeoutException e) {
            future.cancel(true);
            System.out.println("Terminated!");
        }

        executor.shutdownNow();
    }
}

class Task implements Callable<String> {
    @Override
    public String call() throws Exception {
        Thread.sleep(4000); // Just to demo a long running task of 4 seconds.
        return "Ready!";
    }
}

Поиграйте немного с аргументом timeout в методе Future # get () , например увеличьте его до 5, и вы увидите, что резьба закончилась. Вы можете перехватить тайм-аут в блоке catch (TimeoutException e) .

Обновление: чтобы прояснить концептуальное недоразумение, sleep () не требуется . Он просто используется для SSCCE / демонстрационных целей. Просто выполните свою длительную задачу прямо здесь, вместо sleep () . Внутри вашей длительной задачи вы должны проверить, не прерван ли поток следующим образом:

while (!Thread.interrupted()) {
    // Do your long running task here.
}
361
ответ дан 23 November 2019 в 03:04
поделиться

Не существует 100% надежного способа сделать это для любой старой задачи. Задача должна быть написана с учетом этой способности.

Базовые библиотеки Java, такие как ExecutorService , отменяют асинхронные задачи с помощью вызовов interrupt () рабочего потока. Так, например, если задача содержит какой-то цикл, вы должны проверять его статус прерывания на каждой итерации. Если задача выполняет операции ввода-вывода, они также должны быть прерываемыми, а их настройка может быть сложной. В любом случае имейте в виду, что код должен активно проверять прерывания; установка прерывания не обязательно ничего делать.

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

46
ответ дан 23 November 2019 в 03:04
поделиться

Рассмотрите возможность использования экземпляра ExecutorService . Оба метода invokeAll () и invokeAny () доступны с параметром timeout .

Текущий поток будет заблокирован до тех пор, пока метод не завершится (не уверен, желательно ли это), либо из-за того, что задачи завершились нормально, либо из-за того, что истекло время ожидания. Вы можете проверить возвращенные Future (s), чтобы определить, что произошло.

13
ответ дан 23 November 2019 в 03:04
поделиться

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

То, что вы описываете, очень похоже на «рандеву», поэтому вы можете взглянуть на CyclicBarrier .

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

Я обычно рекомендую две книги в этой области: Параллельное программирование в Java и Java Concurrency in Practice .

5
ответ дан 23 November 2019 в 03:04
поделиться

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

[1] map{chr(ord()-1)} ...

Таким образом, последовательность типа «6qD» приведет к «5rC» (символы перед '6', 'q' и 'D' соответственно). Основной интерес представляет массив последовательностей около начала:

[2] ">>>E!)",">>>E)",">>>E",">>>",">>",">",""

Это определяет последовательность «масок», которые мы будем подставлять позже, в эту строку:

[3] "9$_*\x{0e}"

Они будут вставлены в точке $ _ . Последовательности \x {0e} представляет шестнадцатеричный управляющий символ; обратите внимание, что \x {0d} , символ непосредственно перед ним, является возвратом каретки. Это то, что подставится в [3], когда мы сделаем [1].

Перед сборкой последовательности [3] мы добавим число ! равно i для каждого элемента в [2]. Каждый последующий элемент получает еще один ! , чем предшествующий элемент. Обратите внимание, что символ, значение которого находится непосредственно перед ! - космос .

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

[4] "!!!!!9>>>E!)\x{0e}",  ---> "     8===D ("
    "!!!!!!9>>>E)\x{0e}",  ---> "      8===D("
    "!!!!!!!9>>>E\x{0e}",  ---> "       8===D"
    "!!!!!!!!9>>>\x{0e}",  ---> "        8==="
    "!!!!!!!!!9>>\x{0e}",  ---> "         8=="
    "!!!!!!!!!!9>\x{0e}",  ---> "          8="
    "!!!!!!!!!!!9\x{0e}",  ---> "           8"

Затем операция reverse добавляет те же самые элементы в обратном порядке, создавая цикл.

На этом этапе вы должны видеть, как появляется образец, который создает анимацию. Теперь это просто вопрос перемещения через каждый шаг в анимации и обратно, что выполняется остальной сценарий. Задержка времени каждого шага определяется оператором выбора

[5] select undef, undef, undef, 0.25

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

-121--3766495-

Композитный образец - это первое, что приходит мне на ум...

-121--4055801-

Я думаю, что ответ в основном зависит от самой задачи.

  • Она снова и снова выполняет одну задачу?
  • Необходимо ли, чтобы тайм-аут прервал текущую выполняемую задачу сразу после ее истечения?

Если первый ответ - «да», а второй - «нет», вы могли бы сохранить его таким же простым, как это:

public class Main {

    private static final class TimeoutTask extends Thread {
        private final long _timeoutMs;
        private Runnable _runnable;

        private TimeoutTask(long timeoutMs, Runnable runnable) {
            _timeoutMs = timeoutMs;
            _runnable = runnable;
        }

        @Override
        public void run() {
            long start = System.currentTimeMillis();
            while (System.currentTimeMillis() < (start + _timeoutMs)) {
                _runnable.run();
            }
            System.out.println("execution took " + (System.currentTimeMillis() - start) +" ms");
        }

    }

    public static void main(String[] args) throws Exception {
        new TimeoutTask(2000L, new Runnable() {

            @Override
            public void run() {
                System.out.println("doing something ...");
                try {
                    // pretend it's taking somewhat longer than it really does
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }
}

Если это не вариант, пожалуйста, сузьте свои требования - или покажите какой-нибудь код.

0
ответ дан 23 November 2019 в 03:04
поделиться

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

надеюсь это поможет


import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

/**
 * Main class
 * 
 * @author el
 * 
 */
public class Main {
    /**
     * Thread which perform the task which should be timed out.
     * 
     * @author el
     * 
     */
    public static class MainThread extends Thread {
        /**
         * For example reading a file. File to read.
         */
        final private File fileToRead;
        /**
         * InputStream from the file.
         */
        final private InputStream myInputStream;
        /**
         * Thread for timeout.
         */
        final private TimeOutThread timeOutThread;

        /**
         * true if the thread has not ended.
         */
        boolean isRunning = true;

        /**
         * true if all tasks where done.
         */
        boolean everythingDone = false;

        /**
         * if every thing could not be done, an {@link Exception} may have
         * Happens.
         */
        Throwable endedWithException = null;

        /**
         * Constructor.
         * 
         * @param file
         * @throws FileNotFoundException
         */
        MainThread(File file) throws FileNotFoundException {
            setDaemon(false);
            fileToRead = file;
            // open the file stream.
            myInputStream = new FileInputStream(fileToRead);
            // Instantiate the timeout thread.
            timeOutThread = new TimeOutThread(10000, this);
        }

        /**
         * Used by the {@link TimeOutThread}.
         */
        public void kill() {
            if (isRunning) {
                isRunning = false;
                if (myInputStream != null) {
                    try {
                        // close the stream, it may be the problem.
                        myInputStream.close();
                    } catch (IOException e) {
                        // Not interesting
                        System.out.println(e.toString());
                    }
                }
                synchronized (this) {
                    notify();
                }
            }
        }

        /**
         * The task which should be timed out.
         */
        @Override
        public void run() {
            timeOutThread.start();
            int bytes = 0;
            try {
                // do something
                while (myInputStream.read() >= 0) {
                    // may block the thread.
                    myInputStream.read();
                    bytes++;
                    // simulate a slow stream.
                    synchronized (this) {
                        wait(10);
                    }
                }
                everythingDone = true;
            } catch (IOException e) {
                endedWithException = e;
            } catch (InterruptedException e) {
                endedWithException = e;
            } finally {
                timeOutThread.kill();
                System.out.println("-->read " + bytes + " bytes.");
                isRunning = false;
                synchronized (this) {
                    notifyAll();
                }
            }
        }
    }

    /**
     * Timeout Thread. Kill the main task if necessary.
     * 
     * @author el
     * 
     */
    public static class TimeOutThread extends Thread {
        final long timeout;
        final MainThread controlledObj;

        TimeOutThread(long timeout, MainThread controlledObj) {
            setDaemon(true);
            this.timeout = timeout;
            this.controlledObj = controlledObj;
        }

        boolean isRunning = true;

        /**
         * If we done need the {@link TimeOutThread} thread, we may kill it.
         */
        public void kill() {
            isRunning = false;
            synchronized (this) {
                notify();
            }
        }

        /**
         * 
         */
        @Override
        public void run() {
            long deltaT = 0l;
            try {
                long start = System.currentTimeMillis();
                while (isRunning && deltaT < timeout) {
                    synchronized (this) {
                        wait(Math.max(100, timeout - deltaT));
                    }
                    deltaT = System.currentTimeMillis() - start;
                }
            } catch (InterruptedException e) {
                // If the thread is interrupted,
                // you may not want to kill the main thread,
                // but probably yes.
            } finally {
                isRunning = false;
            }
            controlledObj.kill();
        }
    }

    /**
     * Start the main task and wait for the end.
     * 
     * @param args
     * @throws FileNotFoundException
     */
    public static void main(String[] args) throws FileNotFoundException {
        long start = System.currentTimeMillis();
        MainThread main = new MainThread(new File(args[0]));
        main.start();
        try {
            while (main.isRunning) {
                synchronized (main) {
                    main.wait(1000);
                }
            }
            long stop = System.currentTimeMillis();

            if (main.everythingDone)
                System.out.println("all done in " + (stop - start) + " ms.");
            else {
                System.out.println("could not do everything in "
                        + (stop - start) + " ms.");
                if (main.endedWithException != null)
                    main.endedWithException.printStackTrace();
            }
        } catch (InterruptedException e) {
            System.out.println("You've killed me!");
        }
    }
}

С уважением

3
ответ дан 23 November 2019 в 03:04
поделиться

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

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

2
ответ дан 23 November 2019 в 03:04
поделиться

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

ExecutorService executorService = getExecutorService();
Future<SomeClass> future = executorService.submit(new Callable<SomeClass>() {
    public SomeClass call() {
        // Perform long-running task, return result. The code should check
        // interrupt status regularly, to facilitate cancellation.
    }
});
try {
    // Real life code should define the timeout as a constant or
    // retrieve it from configuration
    SomeClass result = future.get(10, TimeUnit.SECONDS);
    // Do something with the result
} catch (TimeoutException e) {
    future.cancel(true);
    // Perform other error handling, e.g. logging, throwing an exception
}

Метод getExecutorService () может быть реализован несколькими способами.Если у вас нет каких-либо конкретных требований, вы можете просто вызвать Executors.newCachedThreadPool () для объединения потоков без верхнего предела количества потоков.

2
ответ дан 23 November 2019 в 03:04
поделиться
Другие вопросы по тегам:

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