Как я могу жестоко и беспощадно прервать задачу в Java? - PullRequest
4 голосов
/ 24 января 2012

Я запрограммировал решатель судоку на Java для домашней работы, и в настоящее время я пытаюсь выяснить, какие проблемы могут возникнуть, чтобы сделать его лучше.Я сгенерировал несколько тысяч сеток судоку с помощью генератора судоку Дэвида Бау , и теперь я запускаю свою программу против них.некоторые из них оказываются проблематичными и делают мой алгоритм поиска сумасшедшим, пока у меня не кончится куча пространства.Таким образом, я подумал, что должен перенести задачу решения во вторичный поток и запустить ее с таймаутом.Прямо сейчас я использую пул потоков из одного потока (в форме ExecutorService) и отправляю ему Callable s.Затем я пытаюсь получить значение с тайм-аутом:

Callable<Long> solveAndReturnTime = new Callable<Long>() { /* snip */ };
Future<Long> time = executor.submit(solveAndReturnTime);
try
{
    long result = time.get(10, TimeUnit.SECONDS);
    System.out.printf("%d millis\n", result);
}
catch (TimeoutException e)
{
    System.err.println("timed out");
    time.cancel(true);
}

Моя проблема в том, что, очевидно, не просто отменяет Future в Java .Future<T>.cancel(boolean) очевидно, не прерывает задачу сразу.Из-за этого пул застрял с нескончаемой задачей, и время последующих попыток истекло, потому что у них никогда не будет шанса на запуск.

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

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

EDIT Мой алгоритм полностью последовательный, не использует глобальный объект и не содержит блокировки.Насколько я могу судить, ничто не пойдет не так, если задача будет отменена в случайный момент;и даже если это так, это не рабочий код.Я готов пройти опасную и коварную прогулку для этого.

Ответы [ 3 ]

5 голосов
/ 24 января 2012

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

Правильное решение проблемы - дополнительная проверка на Thread.currentThread ().isInterrupted () на каждой итерации основного цикла в вас.Callable.Поэтому, когда поток прерывается, он видит его и корректно завершает работу.

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

2 голосов
/ 24 января 2012

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

1 голос
/ 24 января 2012

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

Как отмечают в основном все, нет чистого способа остановить задачу без проверки этой задачи, должна ли она остановиться сама. Я создал класс, который реализует Runnable для выполнения таким образом, что он не будет драматичным, если его убьют. Поле результата (milliseconds) - AtomicLong, поскольку запись в обычные long переменные не гарантируется как атомарная.

class SolveTimer implements Runnable
{
    private String buildData;
    private AtomicLong milliseconds = new AtomicLong(-1);

    public SolveTimer(String buildData)
    {
        assert buildData != null;
        this.buildData = buildData;
    }

    public void run()
    {
        long time = System.currentTimeMillis();
        // create the grid, solve the grid
        milliseconds.set(System.currentTimeMillis() - time);
    }

    public long getDuration() throws ContradictionException
    {
        return milliseconds.get();
    }
}

Мой код создает поток на каждой итерации и запускает SolveTimer. Затем он пытается присоединиться в течение 10 секунд. После возврата join основной поток вызывает getDuration в таймере выполнения; если он возвращает -1, то задача занимает слишком много времени и поток уничтожается.

SolveTimer timer = new SolveTimer(buildData);
Thread worker = new Thread(timer);
worker.start();
worker.join(10000);

long result = timer.getDuration();
if (result == -1)
{
    System.err.println("Unable to solve");
    worker.stop();
}

Следует отметить, что это затрудняет отладку рабочих потоков: когда поток приостанавливается отладчиком, он все еще может быть уничтожен Thread.stop(). На моей машине это пишет короткое сообщение об ошибке ThreadDeath в консоли и приводит к сбою процесса Java.

Существует возможное состояние гонки, при котором рабочий поток завершает точно (или сразу после) getDuration, и из-за этого result будет -1, даже если задача действительно выполнена успешно , Тем не менее, это то, с чем я могу жить: 10 секунд - это уже слишком долго, так что в этот момент мне уже все равно, достаточно ли это почти .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...