28

Is while (true) { ... } loop in threads bad? What's the alternative?

Update; what I'm trying to to...

I have ~10,000 threads, each consuming messages from their private queues. I have one thread that's producing messages one by one and putting them in the correct consumer's queue. Each consumer thread loops indefinitely, checking for a message to appear in their queue and process it.

Inside Consumer.java:

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

The Producer is putting messages inside Consumer message queues at a rapid pace (several million messages per second). Consumers should process these messages as fast as possible!

Note: the while (true) { ... } is terminated by a KILL message sent by the Producer as its last message. However, my question is about the proper way to do this message-passing...

Please see the new question, regarding this design.

4

12 回答 12

19

您可以选择检查中断状态,而不是永远循环并中断或返回。

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

如果您的线程是由 ExecutorService 管理的任务,您可以通过调用 shutdownNow() 让它们全部正常结束。

于 2010-07-29T22:03:49.293 回答
9
while (!stop_running) { ... }

...也许?某种退出标志通常用于控制线程运行。

于 2010-07-29T21:48:34.780 回答
7

不是天生的,没有。您始终可以使用break或保释return。只要确保你真的这样做(在某些时候)

问题是当你的线程无事可做时会发生什么?如果你只是循环检查一个条件,你的线程会吃掉整个 CPU 什么都不做。因此,请确保使用wait来导致您的线程阻塞,或者sleep如果您没有任何东西可以wait打开。

于 2010-07-29T21:49:12.717 回答
3

取决于“坏”的定义。这意味着试图阅读代码的人必须在别处寻找循环终止的原因。这可能会降低它的可读性。

这种极端的心态导致了 COMEFROM 关键字。 http://en.wikipedia.org/wiki/COMEFROM

10 COMEFROM 40
20 INPUT "WHAT IS YOUR NAME? "; A$
30 PRINT "HELLO, "; A$
40 REM
于 2010-07-29T21:58:11.650 回答
1

Usually, you'll want to wait on a resource of some kind to do work, which hides actual threading details from you. It sounds like you wanted to implement your own spinlock.

Here's some tutorial about locking I found I google.

于 2010-07-29T21:46:55.607 回答
1

最好while (...)在线路上有终止条件,但有时终止条件是您只能在循环深处的某个地方测试的东西。那么这break就是(或例外)的目的。实际上,您的线程可能必须永远运行,直到您的程序终止(使用System.exit);那么while (true)绝对是对的。

但也许你在问循环内部应该做什么。您需要确保包含一些阻塞操作,即某个函数调用,您的线程将在其中等待其他人(另一个线程、另一个程序、操作系统)做某事。这通常是Condition.wait在您使用锁进行编程,或从消息队列中读取,或从文件或网络套接字中读取,或其他一些阻塞 I/O 操作时。

请注意,这sleep通常不够好。您无法知道其他参与者何时会做某事,因此无法避免醒来太频繁(从而不必要地消耗 CPU 时间)或太少(因此无法及时对事件做出反应)。始终设计您的系统,以便当线程完成其工作时,它会通知正在等待该工作的任何人(通常Condition.signal是加入或加入)。

于 2010-07-29T21:59:17.660 回答
1

while (true)如果有办法退出循环也不错,否则调用将无限期运行。

对于 10000 个线程来说,while(true)调用是不好的做法……如果线程完成运行,为什么不在sleep()线程上允许其他线程运行或退出策略?

于 2010-07-29T22:07:48.623 回答
1

虽然以上所有答案都是正确的,但我想建议这个,因为我自己也遇到了这种情况:你可以用一个标志说:

isRunning=true;
while(isRunning){
   //do Something
}

稍后,确保在完成从缓冲区或数据文件的读取后将 isRunning 设置为 false。

于 2020-06-29T18:03:52.163 回答
0

我通常使用一个名为“完成”的类属性布尔值,然后线程的运行方法看起来像

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

然后您可以设置 done=true 来终止循环。这可以在循环内部完成,或者您可以使用另一种设置它的方法,以便其他线程可以拔出插头。

于 2010-07-29T22:06:48.303 回答
0

假设标准BlockingQueue. 使用take而不是poll.

除此之外,for (;;)while (true)IMO 更好。

于 2010-07-29T23:31:55.943 回答
0

首先,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--

于 2018-09-23T09:54:53.100 回答
-2

如果我做你所说的,我会试试这个:

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

这使您可以确保您的消息队列上没有任何并发​​修改异常,以及当没有消息时您不会在 while(true) 循环中使用 CPU 时间。现在你只需要确保当你向你的 messageQueue 添加一些东西时你可以调用lock.notifyAll(),以便线程知道再次运行。

于 2010-07-29T22:21:03.950 回答