Java: 'в то время как (верный) {…}' цикл в потоке плохо? Какова альтернатива?

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

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

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

В Consumer.java:

@Override
public void run() {
    while (true) {
        Message msg = messageQueue.poll();
        if (msg != null) {
            ... // do something with the message
        }
    }
}

Производитель помещает сообщения внутренние Потребительские очереди сообщений в быстрый темп (несколько миллионов сообщений в секунду). Потребители должны обработать эти сообщения максимально быстро!

Примечание: while (true) { ... } завершается сообщением УНИЧТОЖЕНИЯ, отправленным Производителем как его последнее сообщение. Однако мой вопрос о надлежащем способе сделать эту передачу сообщений...

Посмотрите новый вопрос, относительно этого дизайна.

26
задан Community 23 May 2017 в 12:10
поделиться

10 ответов

while (!stop_running) { ... }

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

8
ответ дан 28 November 2019 в 07:31
поделиться

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

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

8
ответ дан 28 November 2019 в 07:31
поделиться

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

Вот учебник по блокировке, который я нашел в Google.

1
ответ дан 28 November 2019 в 07:31
поделиться

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

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

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

1
ответ дан 28 November 2019 в 07:31
поделиться

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

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

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

0
ответ дан 28 November 2019 в 07:31
поделиться

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

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

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

-1
ответ дан 28 November 2019 в 07:31
поделиться

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

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

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

18
ответ дан 28 November 2019 в 07:31
поделиться

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

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

10 COMEFROM 40
20 INPUT "WHAT IS YOUR NAME? "; A$
30 PRINT "HELLO, "; A$
40 REM
3
ответ дан 28 November 2019 в 07:31
поделиться

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

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

0
ответ дан 28 November 2019 в 07:31
поделиться

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

Кроме этого, для (;;) лучше, чем , а (true) , IMO.

0
ответ дан 28 November 2019 в 07:31
поделиться