Java: цикл while (true) {...} `в потоке плох?Какая альтернатива? - PullRequest
23 голосов
/ 30 июля 2010

Петля while (true) { ... } в темах плохая?Какая альтернатива?

Обновление;что я пытаюсь ...

У меня ~ 10000 потоков, каждый из которых потребляет сообщения из своих частных очередей.У меня есть один поток, который создает сообщения одно за другим и помещает их в правильную очередь потребителя.Каждый потребительский поток зацикливается на неопределенный срок, проверяя, появилось ли сообщение в его очереди и обрабатывало ли оно.быстрый темп (несколько миллионов сообщений в секунду).Потребители должны обрабатывать эти сообщения как можно быстрее!

Примечание: while (true) { ... } завершается сообщением KILL, отправленным Производителем как его последнее сообщение.Тем не менее, мой вопрос о том, как правильно сделать эту передачу сообщений ...

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

Ответы [ 11 ]

17 голосов
/ 30 июля 2010

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

while (!Thread.currentThread().isInterrupted()) {
    try {
        doWork();
        wait(1000);
    } catch (InterruptedException ex) {
        Thread.currentThread().interrupt();
    }
}

Если ваши потоки являются задачами, управляемыми ExecutorService, вы можете завершить их все просто, вызвав shutdownNow ().

8 голосов
/ 30 июля 2010

Не по своей сути, нет. Вы всегда можете внести залог, используя break или return. Просто убедитесь, что вы действительно (в какой-то момент)

Проблема в том, что происходит, когда ваш поток не имеет ничего общего? Если вы просто проверяете состояние в цикле, ваш поток будет поглощать весь процессор, ничего не делая. Поэтому убедитесь, что вы используете wait, чтобы блокировать ваш поток, или sleep, если у вас нет ничего, чтобы wait on.

8 голосов
/ 30 июля 2010
while (!stop_running) { ... }

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

3 голосов
/ 30 июля 2010

Зависит от определения «плохо». Это означает, что человек, пытающийся прочитать код, должен искать в другом месте причину, по которой цикл прерывается. Это может сделать его менее читабельным.

Этот менталитет довел до крайности результаты в ключевом слове COMEFROM. http://en.wikipedia.org/wiki/COMEFROM

10 COMEFROM 40
20 INPUT "WHAT IS YOUR NAME? "; A$
30 PRINT "HELLO, "; A$
40 REM
1 голос
/ 30 июля 2010

Лучше иметь условие завершения в строке while (...), но иногда условие завершения - это то, что вы можете проверить только где-то глубоко внутри цикла. Тогда это то, что break для (или исключения). На самом деле, возможно, ваш поток должен работать вечно, пока ваша программа не завершится (с System.exit); тогда while (true) определенно прав.

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

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

0 голосов
/ 23 сентября 2018

Прежде всего, прямой ответ на эту проблему от Dough Lea:

Практически никогда не стоит использовать голые спины в ожидании значений переменных. Используйте Thread.onSpinWait, Thread.yield и / или блокировку синхронизации, чтобы лучше справляться с тем фактом, что «в конечном итоге» может потребоваться много времени, особенно когда в системе больше потоков, чем ядер.

http://gee.cs.oswego.edu/dl/html/j9mm.html

Thead.onSpinWait был представлен в Java 9. Это может выглядеть так.

while (true) {
    while (messageQueue.peek() == null) {
       Thread.onSpinWait();
    }
    // do something with the message
}

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

https://docs.oracle.com/javase/9/docs/api/java/lang/Thread.html#onSpinWait--

0 голосов
/ 30 июля 2010

Похоже, вы заняты ожиданием, принимая стандарт BlockingQueue. Используйте take вместо poll.

Кроме этого, for (;;) лучше, чем while (true), ИМО.

0 голосов
/ 30 июля 2010

Если бы я делал то, о чем вы говорите, я бы попробовал это:

private Object lock = new Object();    

public void run(){
    while(true){
        synchronized(lock){
            Message msg = messageQueue.poll();
            if (msg != null) {
                ... // do something with the message
            }else{
                try{
                    lock.wait();
                }catch(InterruptedException e){
                    e.printStackTrace();
                    continue;
                }
            }
        }
    }
}

Это позволяет вам убедиться в том, что вы не получите никакого исключения для одновременной модификации вашего сообщенияQueue, а также когданет сообщения, что вы не будете использовать процессорное время в цикле while (true).Теперь вам просто нужно убедиться, что когда вы добавляете что-то в ваше сообщениеQueue, вы можете вызвать lock.notifyAll(), чтобы поток знал, что он снова запустится.

0 голосов
/ 30 июля 2010

while (true) неплохо, если есть способ выйти из цикла, иначе вызов будет выполняться бесконечно.

Для 10000 потоков выполнение вызова while(true) является плохой практикой ... почему у вас нет sleep() в потоке, позволяющем запускать другие потоки, или стратегии выхода, если поток завершает работу?

0 голосов
/ 30 июля 2010

Обычно я использую логический атрибут класса с именем «done», тогда методы запуска потоков выглядят как

done = false;
while( !done ) {
    // ... process stuff
}

Затем вы можете установить done = true, чтобы завершить цикл. Это может быть сделано из цикла, или у вас может быть другой метод, который устанавливает его так, чтобы другие потоки могли отключить.

...