循环中的技术非终止条件
我们大多数人都知道循环不应该有非终止条件。例如,此 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 访问,这还重要吗?
编辑: 我主要担心的问题之一是不可预见的逻辑错误可能会导致非终止条件。通常,如果确保不存在非终止条件,则可以更优雅地识别或处理这些逻辑错误,但这值得吗?什么时候?这是与信任正交的担忧。
Most of us know that a loop should not have a non-terminating condition. For example, this C# loop has a non-terminating condition: any even value of i. This is an obvious logic error.
void CountByTwosStartingAt(byte i) { // If i is even, it never exceeds 254
for(; i < 255; i += 2) {
Console.WriteLine(i);
}
}
Sometimes there are edge cases that are extremely unlikeley, but technically constitute non-exiting conditions (stack overflows and out-of-memory errors aside). Suppose you have a function that counts the number of sequential zeros in a stream:
int CountZeros(Stream s) {
int total = 0;
while(s.ReadByte() == 0) total++;
return total;
}
Now, suppose you feed it this thing:
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)
}
}
Or more realistically, maybe a stream that returns data from external hardware, which in certain cases might return lots of zeros (such as a game controller sitting on your desk). Either way we have an infinite loop. This particular non-terminating condition stands out, but sometimes they don't.
A completely real-world example as in an app I'm writing. An endless stream of zeros will be deserialized into infinite "empty" objects (until the collection class or GC throws an exception because I've exceeded two billion items). But this would be a completely unexpected circumstance (considering my data source).
How important is it to have absolutely no non-terminating conditions? How much does this affect "robustness?" Does it matter if they are only "theoretically" non-terminating (is it okay if an exception represents an implicit terminating condition)? Does it matter whether the app is commercial? If it is publicly distributed? Does it matter if the problematic code is in no way accessible through a public interface/API?
Edit:
One of the primary concerns I have is unforseen logic errors that can create the non-terminating condition. If, as a rule, you ensure there are no non-terminating conditions, you can identify or handle these logic errors more gracefully, but is it worth it? And when? This is a concern orthogonal to trust.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您要么“信任”您的数据源,要么不“信任”。
如果您信任它,那么您可能会想尽最大努力来处理数据,无论数据是什么。如果它永远给你发送零,那么它给你带来了一个太大的问题,你的资源无法解决,你花费了你所有的资源来解决它,但失败了。你说这是“完全出乎意料的”,所以问题是,仅仅“完全出乎意料的”你的应用程序因为内存不足而崩溃是否可以。或者它实际上是不可能的吗?
如果您不信任您的数据源,那么您可能需要人为地限制您将尝试的问题的大小,以便在系统内存耗尽之前失败。
无论哪种情况,都可以编写您的应用程序,使您可以从内存不足异常中正常恢复。
无论哪种方式,这都是一个鲁棒性问题,但是由于问题太大而无法解决(您的任务是不可能的)而失败通常被认为比因为某些恶意用户向您发送零流而失败(您接受了一个不可能的任务)更容易接受一些 script-kiddie DoS 攻击者)。
You either "trust" your data source, or you don't.
If you trust it, then probably you want to make a best effort to process the data, no matter what it is. If it sends you zeros for ever, then it has posed you a problem too big for your resources to solve, and you expend all your resources on it and fail. You say this is "completely unexpected", so the question is whether it's OK for it to merely be "completely unexpected" for your application to fall over because it's out of memory. Or does it need to actually be impossible?
If you don't trust your data source, then you might want to put an artificial limit on the size of problem you will attempt, in order to fail before your system runs out of memory.
In either case it might be possible to write your app in such a way that you recover gracefully from an out-of-memory exception.
Either way it's a robustness issue, but falling over because the problem is too big to solve (your task is impossible) is usually considered more acceptable than falling over because some malicious user is sending you a stream of zeros (you accepted an impossible task from some script-kiddie DoS attacker).
诸如此类的事情必须根据具体情况来决定。如果进行额外的健全性检查可能是有意义的,但要使每段代码完全万无一失,工作量太大;而且并不总是能够预测傻瓜会想出什么办法。
Things like that have to decided on a case-by-case basis. If may make sense to have additional sanity checks, but it is too much work too make every piece of code completely foolproof; and it is not always possible to anticipate what fools come up with.
我想说,您要么“支持”与该数据源一起使用的软件,要么不支持。例如,我见过一些软件不能处理内存不足的情况:但该软件不“支持”内存不足(或者更具体地说,该系统不支持内存不足) ;因此,对于该系统,如果出现内存不足的情况,修复方法是减少系统负载或增加内存(而不是修复软件)。对于该系统,处理内存不足并不是一个要求:要求的是管理系统上的负载,并为给定的负载提供足够的内存。
I'd say that you either "support" the software being used with that data source, or you don't. For example I've seen software which doesn't handle an insufficient-memory condition: but insufficient memory isn't "supported" for that software (or less specifically it isn't supported for that system); so, for that system, if an insufficient-memory condition occurs, the fix is to reduce the load on the system or to increase the memory (not to fix the software). For that system, handling insufficient memory isn't a requirement: what is a requirements is to manage the load put on the system, and to provide sufficient memory for that given load.
这根本不重要。也就是说,它本身并不是一个目标。重要的是代码正确地实现了规范。例如,如果主循环确实终止,则交互式 shell 可能存在错误。
在您描述的场景中,无限零的问题实际上是内存耗尽的特殊情况。这不是一个理论上的问题,而是实际可能发生的事情。您应该决定如何处理这个问题。
It isn't important at all. That is, it's not a goal by itself. The important thing is that the code correctly implements the spec. For example, an interactive shell may have a bug if the main loop does terminate.
In the scenario you're describing, the problem of infinite zeros is actually a special case of memory exhaustion. It's not a theoretical question but something that can actually happen. You should decide how to handle this.