已校对

This commit is contained in:
Rei 2014-07-28 17:27:11 +08:00
parent f134b8c9d9
commit 04c724dbe3

View File

@ -1,30 +1,30 @@
编程的乐趣:快速出错
编程的乐趣:快速终止
================================================================================
![](http://www.opensourceforu.com/wp-content/uploads/2011/12/fail-350x262.jpg)
> 当软件出现问题的时候,它应该以一种能引起注意的方式马上终止。这种“快速出错”的方式值得借鉴,我们会在这期专栏里谈谈这个重要的概念。
> 当软件出现问题的时候,它应该以一种很容易引起注意的方式马上终止。这种“快速终止”的方式值得借鉴,我们会在这期专栏里谈谈这个重要的概念。
一开始,“快速出错”看上去是一种会影响可靠性的不好的实践-为什么一个系统在还可以继续运行的时候要崩溃(或者说终止)?对于这个,我们需要理解,快速出错是和Heisenbugs对于不能复现bug的一种称呼紧密联系在一起的。
一开始,“快速终止”看上去是一种会影响可靠性的不好的实践——为什么一个系统在还可以继续运行的时候要崩溃(或者说终止)?对于这个,我们需要理解,快速终止是和Heisenbugs对于不易复现bug的一种称呼紧密联系在一起的。
考虑一下Bohrbugs对于能够重现的bug的一种称呼它们在给定输入的时候总是会出现比如访问空指针。这类问题很容易测试复现并修复。如今所有有经验的程序员应该都面对过这样的情形导致崩溃的bug在重启软件后不再出现了。不管花多少时间或努力去重现问题那个bug就是跟我们捉迷藏。这种bug被称为Heisenbugs。
考虑一下Bohrbugs对于能够重现的bug的一种称呼它们在一个给定输入的条件下总是会出现比如访问空指针。这类问题很容易测试、复现并修复。而如今所有有经验的程序员应该都面对过这样的情形导致崩溃的bug在重启软件后就不再出现了。不管花多少时间或努力去重现问题那个bug就是跟我们捉迷藏。这种bug被称为Heisenbugs。
花在寻找修复和测试Heisenbugs上的努力比起Bohrbugs来说要高出一个数量级。一种避免Heisenbugs的策略是将它们转化为Bohrbugs。怎么做呢预测可能导致Heisenbugs的因素然后尝试将它们变成Bohrbugs。是的这并不简单而且也并不是一定就能成功,但是让我们来看一个能产生效果的特殊例子。
花在寻找修复和测试Heisenbugs上的努力比起Bohrbugs来说要高出一个数量级。一种避免Heisenbugs的策略是将它们转化为Bohrbugs。怎么做呢预测可能导致Heisenbugs的因素然后尝试将它们变成Bohrbugs。是的这并不简单而且也并不是一定可行,但是让我们来看一个能产生效果的特殊例子。
并发编程是Heisenbugs经常出现的一个典范。我们的例子就是一个Java里和并发相关的问题。在遍历一个Java集合的时候一般要求只能通过Iterator的方法比如remove()方法。而当遍历的时候,如果有另一个线程尝试修改底层集合(因为编程时留下的错误),那么底层集合就可能会被破坏(例如,导致不正确的状态)。
并发编程是Heisenbugs经常出现的一个典范。我们的例子就是一个Java里和并发相关的问题。在遍历一个Java集合的时候一般要求只能通过Iterator的方法对集合进行操作比如remove()方法。而在遍历期间,如果有另一个线程尝试修改底层集合(因为编程时留下的错误),那么底层集合就可能会被破坏(例如,导致不正确的状态)。
类似这种不正确的状态会导致不确定的错误假如我们幸运的话实际上这很不幸程序可以继续执行而不会崩溃但是却给出错误的结果。这种bug很难重现和修复因为这一类的程序错误都是不确定的。换句话说这是个Heisenbug。
类似这种不正确的状态会导致不确定的错误——假如我们幸运的话实际上这很不幸程序可以继续执行而不会崩溃但是却给出错误的结果。这种bug很难重现和修复因为这一类的程序错误都是不确定的。换句话说这是个Heisenbug。
幸运的是Java Iterators会尝试侦测这种并发修改在发现了以后,会丢出异常`ConcurrentModificationException`而不是等到最后再出错那样也是没有任何迹象的。换句话说Java Iterators也遵从了“快速出错”的方法。
幸运的是Java Iterators会尝试侦测这种并发修改并且当发现时,会抛出异常`ConcurrentModificationException`而不是等到最后再出错——那样也是没有任何迹象的。换句话说Java Iterators也遵从了“快速终止”的方法。
如果异常`ConcurrentModificationException`在正式软件中发生了呢根据在Javadoc里对这个异常的说明它“只应该用于侦测bug”。换句话说`ConcurrentModificationException`只应该在开发阶段监听和修复,而不应该泄漏到正式代码中。
如果一个`ConcurrentModificationException`异常在正式版软件中发生了呢根据在Javadoc里对这个异常的说明它“只应该用于侦测bug”。换句话说`ConcurrentModificationException`只应该在开发阶段监听和修复,而不应该泄漏到正式代码中。
好吧如果正式软件确实发生了这个异常那它当然是软件中的bug应当报告给开发者并修复。至少我们能够知道发生了一次底层数据结构的并发修改,而这是软件出错的原因(而不是让软件产生错误的结果,或是以其他现象延后出错,这样就很难跟踪到根本原因)。
好吧如果正式软件确实发生了这个异常那它当然是软件中的bug应当报告给开发者并修复。至少我们能够知道曾经发生过一次针对底层数据结构的并发修改尝试,而这是软件出错的原因(而不是让软件产生错误的结果,或是以其他现象延后出错,这样就很难跟踪到根本原因)。
安全出错”的方法意味着开发健壮的代码。一个很好的编写安全出错代码的例子就是使用断言。很可惜的是,关于断言的使用有大量不必要的公开争论。其中主要的批评点是:它在开发版本中使用,而在发布版中却被关掉的。
防止崩溃”的途径就意味着开发健壮的代码。一个很好的编写容错代码的例子就是使用断言。很可惜的是,关于断言的使用有大量不必要的公开争论。其中主要的批评点是:它在开发版本中使用,而在发布版中却被关掉的。
不管怎么样这个批评是错误的从来没有说用断言来替代应该放到发布版软件中的防御式检查代码。例如断言不应该用来检查传递给函数的参数是否为空。相应的应该用一个if语句来检查这个参数是否正确否则的话抛出异常或是提前返回来适合上下文。然而断言一般用于额外检查代码中所做出的假设,它们应该为真才正常。例如,用一个语句来检查在进行了入栈操作后,栈应该不是空的(例如,对“不变量”的检查)。
不管怎么样,这个批评是错误的:从来没有说用断言来替代应该放到发布版软件中的防御式检查代码。例如断言不应该用来检查传递给函数的参数是否为空。相应的应该用一个if语句来检查这个参数是否正确否则的话抛出一个异常,或是提前返回,来适合上下文。然而,断言一般可以用于额外检查代码中所作出的假设,这些假设应该一直为真才正常。例如,用一个语句来检查在进行了入栈操作后,栈应该不是空的(例如,对“不变量”的检查)。
所以,快速出错,随时中断,那么你已经走在开发更加健壮代码的道路上了。
所以,快速终止,随时中断,那么你就走在开发更加健壮代码的道路上了。
@ -32,7 +32,7 @@
via:http://www.opensourceforu.com/2011/12/joy-of-programming-fail-fast/
译者:[zpl1025](https://github.com/zpl1025) 校对:[校对者ID](https://github.com/校对者ID)
译者:[zpl1025](https://github.com/zpl1025) 校对:[ReiNoir](https://github.com/reinoir)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](http://linux.cn/) 荣誉推出