0

我们大多数人都知道循环不应该有非终止条件。例如,这个 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

4 回答 4

4

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

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

如果您不信任您的数据源,那么您可能想要人为地限制您将尝试的问题的大小,以便在系统内存不足之前失败。

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

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

于 2009-12-05T19:12:19.440 回答
0

诸如此类的事情必须根据具体情况来决定。如果进行额外的健全性检查可能是有意义的,但要使每段代码完全万无一失,工作量太大;并且并不总是能够预测傻瓜会想出什么。

于 2009-12-05T19:17:09.617 回答
0

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

我会说您要么“支持”与该数据源一起使用的软件,要么不支持。例如,我见过不处理内存不足情况的软件:但该软件不“支持”内存不足(或者更具体地讲,该系统不支持内存不足);因此,对于该系统,如果出现内存不足的情况,解决方法是减少系统负载或增加内存(而不是修复软件)。对于该系统,处理内存不足不是必需的:要求管理系统上的负载,并为给定的负载提供足够的内存。

于 2009-12-05T19:55:32.580 回答
0

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

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

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

于 2009-12-05T21:29:29.583 回答