Прервать цикл по истечении фиксированного времени

#java #loops #concurrency

#java #циклы #параллелизм

Вопрос:

У меня есть поток, в котором у меня есть бесконечный цикл, выполняющий некоторые сетевые операции. Похоже, что я не получаю ответа каждый раз, когда делаю это, поэтому поток зависает на несколько секунд, что вызывает серьезные проблемы с моим программным обеспечением. Что мне нужно, так это какой-то «крайний срок» для цикла, если это займет больше времени (например, 100 мс), перезапустите снова.

 private boolean active = true;
public void run(){

   while(active){
       //some network stuff e.g:
       dnshandler.reverselookup("8.8.8.8");
   }

}
  

(это не настоящий класс… это просто для того, чтобы получить представление о том, что я имею в виду.)

Есть идеи, как с этим справиться?

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

 ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        try {
            List<Future<String>> results = executor.invokeAll(Arrays.asList(new CallableClass()), 500), TimeUnit.MILLISECONDS);
            for (Future<String> current : results) {
                if (!current.isCancelled()) {
                    someValue = current.get();
                } else {
                    // timeout
                    executor.shutdownNow();
                }
            }

        } catch (Exception e) {
            //handle it!
            e.printStackTrace();
        }
  

Но проблема, с которой я сталкиваюсь сейчас, заключается в том, что executor.shutdownNow() не завершается зависающая вызываемая задача (что является правильным поведением в соответствии с документацией). Есть ли способ завершить задачу исполнителя? (Я знаю, что это не чистое решение, но некоторые запросы обрабатываются библиотекой)

Ответ №1:

Вы можете поместить свои сетевые материалы в отдельный поток и запустить его, например, на пару секунд:

 int timeoutTime = ...
Runnable networkingStuff = ... // put the networking in here
Thread thread =new Thread(networkingStuff);
thread.start();
try {
    thread.join(timeoutTime);
    if(thread.isAlive()) {
        thread.interrupt();
    }
} catch (InterruptedException e) {
    // catch here the interruption
 }
  

Комментарии:

1. Я не думал о join, это могло бы сработать. Просто для подтверждения, если «сетевой поток» завершается в timeoutTime я в конечном итоге попадаю в часть try блока try-catch, else в catch. В любом случае мой бесконечный цикл продолжается?

2. Вы попадаете в блок catch, если поток прерывается ненормально. Это не должно происходить в нормальных рабочих условиях. Вы всегда должны заканчиваться в «if (thread.IsAlive())» при nprmal условиях. Если поток завершился сам по себе, то thread.IsAlive() == false. Если он все еще выполняется после истечения времени ожидания, то thread.IsAlive() == true и вы завершаете поток с помощью thread.interrupt(). В вашем бесконечном цикле вы должны проверить наличие Thread.interrrupted() == true. Если это так, вы должны завершить цикл, вызвав break или установив переменную, которая проверяется в условиях цикла.

3. Хорошо, спасибо. Я реализовал это так, как вы предполагали, и это работает локально. Я добавлю это в следующий тестовый выпуск и сообщу вам, как это сработало при полной загрузке. Спасибо!

Ответ №2:

TimeLimiter от Google Guava может сделать то, что вам нужно.

[TimeLimiter] создает прокси, которые накладывают ограничение по времени на вызовы методов к проксируемому объекту.

Ответ №3:

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

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

Это проблема не с циклом, а скорее с методами, которые вы вызываете внутри.

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

Комментарии:

1. Я думал о том, чтобы запускать поток каждый раз, когда я вызываю трудоемкие методы, потому что это своего рода стандартный подход для обработки блокирующих функций. Но я немного боюсь запускать несколько сотен потоков в секунду, или это не проблема? В любом случае, я рассмотрю подход TimeoutException и сообщу об этом. Спасибо 🙂

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

3. Весь цикл уже находится в отдельном потоке. У меня есть поток-производитель и несколько потоков-потребителей. Упомянутый поток является потоком-потребителем. Проблемы возникают, если все потоки-потребители зависают из-за отсутствия ответа, это приводит к исключению нехватки памяти (которого можно избежать, используя меньший буфер между производителем или потребителем ( —> приводит к потере данных, поскольку производитель не может произвести) или увеличению размера кучи, что скорее является обходным решением и приводит к другим проблемам). Я все еще пытаюсь поймать TimeoutException … позже опубликую свой прогресс.

4. Справедливо, извините, я не смог быть более конкретным или предоставить какой-либо код — как я уже сказал, не разработчик Java, просто пытаюсь помочь! ^_^

Ответ №4:

Ваш бесконечный цикл while загружает процессор. Попробуйте добавить задержку в 50 мс или около того, чтобы дать небольшой перерыв процессору для решения других задач. А еще лучше, запланируйте выполнение своей задачи с помощью Timer с определенной задержкой и TimerTask .

Ответ №5:

Вы должны прервать поток после истечения времени ожидания. Ознакомьтесь с руководством по java concurrency.