循环中的技术非终止条件

时间:2009-12-05 18:55:44

标签: language-agnostic loops infinite-loop

我们大多数人都知道循环不应该具有非终止条件。例如,此C#循环具有非终止条件:i的任何偶数值。这是一个明显的逻辑错误。

void CountByTwosStartingAt(byte i) { // If i is even, it never exceeds 254
    for(; i < 255; i += 2) { 
        Console.WriteLine(i);
    }
}

有时边缘情况极其不同,但在技术上构成了非退出条件(堆栈溢出和内存不足错误)。假设您有一个函数来计算流中顺序零的数量:

int CountZeros(Stream s) {
    int total = 0;
    while(s.ReadByte() == 0) total++;
    return total;
}

现在,假设你喂这个东西:

class InfiniteEmptyStream:Stream
{
    // ... Other members ...

    public override int Read(byte[] buffer, int offset, int count) {      
        Array.Clear(buffer, offset, count); // Output zeros
        return count; // Never returns -1 (end of stream)
    }
}

或者更现实地说,可能是从外部硬件返回数据的流,在某些情况下可能会返回大量零(例如桌面上的游戏控制器)。无论哪种方式,我们都有一个无限循环。这种特殊的非终止条件很突出,但有时它们不会

一个完全真实的例子,就像我正在写的应用程序一样。无休止的零流将被反序列化为无限的“空”对象(直到集合类或GC抛出异常,因为我已经超过了20亿个项目)。但这将是一个完全出乎意料的情况(考虑到我的数据来源)。

绝对没有非终止条件有多重要?这对“稳健性”有多大影响?如果它们只是“理论上”非终止是否重要(如果异常代表一个隐含的终止条件,它是否可以)?该应用程序是否商业化是否重要?如果是公开发行的?如果有问题的代码无法通过公共接口/ API访问,这是否重要?

修改 我所关注的主要问题之一是未预见到可能产生非终止条件的逻辑错误。通常,如果您确保没有非终止条件,您可以更优雅地识别或处理这些逻辑错误,但它是否值得?什么时候?这是与信任正交的问题。

4 个答案:

答案 0 :(得分:4)

您要么“信任”您的数据源,要么“不信任”。

如果您信任它,那么您可能希望尽最大努力处理数据,无论它是什么。如果它永远向你发送零,那么它就会给你一个太大的问题,无法解决你的资源,并且你将所有资源都花在了它上面并且失败了。你说这是“完全出乎意料的”,所以问题在于它是否可以仅仅因为内存不足而“完全出乎意料”。或者它实际上是不可能的?

如果您不信任您的数据源,那么您可能希望对您将尝试的问题的大小设置一个人为限制,以便在系统内存不足之前失败。

在任何一种情况下,您都可以编写应用程序,以便从内存异常中优雅地恢复。

无论哪种方式都是一个健壮性问题,但由于问题太大而无法解决(你的任务不可能)通常被认为比摔倒更容易接受,因为一些恶意用户正在向你发送一串零(你接受了来自一些脚本小子DoS攻击者的不可能的任务。)

答案 1 :(得分:0)

这样的事情必须根据具体情况决定。如果有额外的理智检查可能有意义,但是太多的工作也使每一段代码完全万无一失;并不总是能够预见到傻瓜会想出什么。

答案 2 :(得分:0)

  

您要么“信任”您的数据源,要么“不信任”。

我要说你“支持”与该数据源一起使用的软件,或者你不支持。例如,我见过的软件无法处理内存不足的情况:但是该软件“不支持”内存不足(或者系统不支持该内存) ;因此,对于该系统,如果发生内存不足的情况,则修复是减少系统负载或增加内存(而不是修复软件)。对于该系统,处理不足的内存不是必需的: 的要求是管理系统上的负载,并为给定的负载提供足够的内存。

答案 3 :(得分:0)

  

绝对有多重要   没有非终止条件?

根本不重要。也就是说,它本身并不是一个目标。重要的是代码正确实现了规范。例如,如果主循环终止,交互式shell可能会有错误。

在您描述的场景中,无限零的问题实际上是内存耗尽的一个特例。这不是一个理论问题,而是实际可能发生的事情。你应该决定如何处理这个问题。