可怕的 While (True) 循环的最佳重构

Best refactoring for the dreaded While (True) loop

提问人:Jeff Schumacher 提问时间:10/29/2008 最后编辑:Jeff Schumacher 更新时间:9/28/2019 访问量:17228

问:

如果你像我一样,在While(True)循环的现场瑟瑟发抖,那么你也一定已经认真思考了重构它的最佳方法。我见过几种不同的实现,没有一个比其他任何实现都好,比如计时器和委托组合。

那么,你想出或看到的重构可怕的 While (True) 循环的最佳方法是什么?

编辑:正如一些评论所提到的,我的意图是让这个问题成为一个“无限循环”重构,例如运行一个 Windows 风格的服务,其中唯一的停止条件是 OnStop 或致命的异常。

重构 循环 while-loop

评论

0赞 Jeff Schumacher 10/31/2008
我应该把这个问题写得更好,并考虑过回去做这件事,但这是一个不断学习的过程,所以我决定把这个问题原封不动地留给自己。:)
3赞 Tyler Durden 2/21/2017
如果你“害怕”while(true),你一定是在写非常简单的算法。大多数高级算法都需要具有多个退出条件的循环。最自然的方法是有一个外部无限循环,然后有多个退出条件来打破循环。当只有一个退出条件时,使用 For 循环或 while 条件效果最佳。虽然这是最常见的情况,但这并不是普遍的情况。

答:

23赞 Konrad Rudolph 10/29/2008 #1

这有什么可怕的?尝试找到一个常见的中断条件,并将其重构为循环的头部。如果这是不可能的——很好。

评论

6赞 Torlack 10/29/2008
或者重构,使其位于最后并利用“do while”。
14赞 Jon B 10/29/2008 #2

将 True 替换为要用于跳出循环的条件。

对于服务或后台线程,可以使用:

volatile bool m_shutdown = false;
void Run()
{
    while (!m_shutdown)
    { ... }
}

评论

2赞 Grank 10/29/2008
有时人们会为服务这样做,其中执行意味着无限期地继续,并且只会被破坏 OnStop 或异常。所以没有条件。
1赞 Keith Nicholas 10/29/2008
我认为这个问题更针对无限循环的半常见实践,当你实际上打算做有限的次数(忽略线程等)时,它可能只是最终条件很复杂。
0赞 Torlack 10/29/2008
接下来,有“BEGIN X WHILE Y REPEAT”,其中条件位于循环的中间。这种类型的循环结构不容易转换为更传统的“while{}”或“do {} while”循环。
1赞 Grank 10/29/2008
我不同意;提到Timer & Delegate Method似乎表明,这个问题更针对的是期望的无限循环
0赞 Jeff Schumacher 10/29/2008
@Grank - 是的!我需要编辑这个问题以使其更具体。
5赞 Keith Nicholas 10/29/2008 #3

errr,要进行重构.....

  • 将无限循环替换为无限递归 :-)

好吧,如果你有一种支持 Tail 调用的语言......

18赞 Jim Nelson 10/29/2008 #4

当我遇到 while(true) 循环时,它会告诉我

  1. 在回路的顶部(或底部)不容易测试断裂条件,
    • 有多种断裂条件,
    • 或者之前的程序员懒得正确地分解循环。

1 和 2 表示您不妨坚持使用 while(true)。(我用 ,但在我看来,这是一种风格。我和另一张海报在一起,为什么要害怕这个?我害怕被折磨的循环,它们跳过箍以“正确”滚动循环。for(;;)

4赞 Grank 10/29/2008 #5

如果你想让它无限期地持续下去,直到程序流完全流产,我看不出while(true)有什么问题。我最近在 .NET 数据收集服务中遇到了它,该服务将 while (true) 与 thread.sleep 相结合,每分钟唤醒一次并轮询第三方数据服务以获取新报告。我考虑过用计时器和委托重构它,但最终决定这是最简单、最容易阅读的方法。10 次中有 9 次是清晰的代码气味,但是当没有退出条件时,为什么要让事情变得更加困难?

8赞 S.Lott 10/29/2008 #6

“永远运行”的情况有时是更大的状态机的一部分。许多嵌入式设备(具有永远运行循环)并不是真正永远运行。它们通常具有多种操作模式,并将在这些模式之间排序。

当我们构建热泵控制器时,有一个运行了一段时间的自检电源 (POST) 模式。然后有一个初步的环境收集模式,直到我们弄清楚所有的区域和恒温器等等。

一些工程师声称,接下来是“永远运行”的循环。事情并没有那么简单。实际上是几种操作模式翻转和翻转。有加热、除霜、冷却、怠速和其他东西。

我倾向于将“永远”循环视为一种操作模式——在未来的某个时候可能会有其他操作模式。

someMode= True
while someMode:
    try:
        ... do stuff ...
    except SomeException, e:
        log.exception( e )
        # will keep running
    except OtherException, e:
        log.info( "stopping now" )
        someMode= False

在某些情况下,到目前为止,我们没有看到任何设置。但我喜欢假装在未来的某个版本中会进行模式更改。someModeFalse

14赞 Ken Ray 10/29/2008 #7

为什么要重构?这个结构有什么“可怕”的?它被广泛使用,并且很好理解。

没坏就别修。

评论

0赞 Tadgh Wagstaff 3/21/2022
我会把这种观点更进一步——衡量成功的主要标准是工作代码。有时,虽然(true)是显而易见的解决方案,但在很多环境中,“它有效且易于阅读”就足够了
2赞 Chris Cudmore 10/29/2008 #8

当无限循环包含在窗口中并随着窗口一起消失时,我不介意它。

想想哈塞尔霍夫递归。

8赞 Andrew Coleson 10/29/2008 #9
#define ever 1
for (;ever;)

?

嗯,就让它保持原样,而(真实)可能和你将要得到的一样清晰。

评论

1赞 Chris Cudmore 11/4/2008
我讨厌滥用这样的“for”循环。有一个语义,“for”循环是为其设计的(本质上是一个从 - 到语义),我认为我们应该坚持这一点。您在这里所做的是将“while”语义强制到“for”关键字中。至少 #define 使它勉强可以忍受。
8赞 utku_karatas 12/12/2008
如果我们要变得那么丑陋,为什么不“永远 #define(1)”或类似的东西:)
1赞 ShellCode 5/26/2021
更短更丑:for(;"ever";)
64赞 fizzer 10/29/2008 #10

我的偏好是

start:

   // code goes here

goto start;

这最清楚地表达了意图。祝你好运,让它通过你的编码标准。(想知道这会让我付出多少业力)。

评论

6赞 Konrad Rudolph 10/29/2008
这如何比循环更好地表达你的意图?另一方面,您没有提到解决方案的缺点。所以是的,总而言之,这是一个非常糟糕的代码。while(true)
26赞 fizzer 10/29/2008
意图是启动的无条件分支。“goto”是专门用于此目的的。'While' 支持条件分支。通过使用退化的 always-true 条件调用它,并依靠编译器来优化它,你就离表达你的意思多了一步。
7赞 fizzer 10/29/2008
至于缺点,没有技术缺点。如果您需要示波器,请放入一些大括号。如果你不这样做,就不要。(假设 C 或 C++ - OP 不指定语言)。
5赞 Amarghosh 5/28/2010
你将需要另一个人来打破这个循环......变得复杂?goto
9赞 Dan Moulding 8/18/2010
@Amarghosh:如果你需要跳出循环,那么它就不是一个真正的无限循环;如果你用这些东西,你就做错了什么。while (true)
28赞 artur02 10/29/2008 #11

我们真的需要重构while(true)循环吗? 有时它是一种编码标准,大多数开发人员已经习惯了这种结构。如果你必须认真思考如何重构这段代码,你确定重构它是个好主意吗?

Goto曾经是编码标准中的害群之马。我遇到过 goto 使代码更具可读性和更短的算法。有时不值得重构(或者最好使用 goto)。

另一方面,大多数时候你可以避免while(true)。

-2赞 12431234123412341234123 5/18/2016 #12
void whiletrue_sim(void)
  {
    //some code
    whiletrue_sim();
  }

警告:您的堆栈可能会溢出。

评论

0赞 Rakete1111 5/18/2016
虽然这个代码块可以回答这个问题,但如果你能提供一些解释来解释为什么它这样做,那将是最好的。
0赞 12431234123412341234123 6/9/2016
只需阅读代码即可。我不善于用词,如果你不理解这个简单的代码,我无法更好地解释它。它只是为了让你的堆栈溢出,这取决于语言、优化器或解释器的工作方式。