每行代码都有潜在的Bug

发表于:2015-5-12 11:10

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:乐徒    来源:51Testing软件测试网采编

  去年夏天我写了一些代码来实现从一个哈希表中获取一条消息。这条消息是将要通过另外一个线程放入哈希表中的。这里会有很小的概率发生冲突,即一开始查找消息的时候它还没有被保存进去。查找的代码如下:
  while ((message = map.get(key)) == null && System.currentTimeMillis() < timeoutTime) {
  wait(1000);
  }
  wait() 函数调用阻塞当前线程,等待负责向表存入消息的线程调用 notifyAll() 函数。这里 1000 表示 1 秒。大约 5 秒后将会超时。
  上面的代码是简单而且正确的。它将会一直保持循环,直到获取得到数值或者超时。超时最多延迟1秒,但在这个案例中不会有问题。(或者说,直到超时发生,你才会遇到更多严重的问题)
  代码被另外两个人评审。两个人都抱怨 wait() 函数需要等待”当前”到超时之间的整个时长,而不是仅仅 1 秒。他们认为我的代码不必要地唤醒了线程五次。我的回复是只有在第一秒内是最有可能读取到消息的,而且唤醒一个线程的代价并不大。我认为他们提出的代码会太复杂,而且更有可能存在bug。
  他们都说,“一个减法并不复杂”,然后就回到了自己的座位上将他们修改后的版本通过邮件发送给我,想要以此证明他们的办法是多么简单。两个人的代码分别都出现了一个 bug。第一个人的 bug很简单:他使用了错误的常量进行计算。但第二个人的bug却非常微妙:
  while ((message = map.get(key)) == null && System.currentTimeMillis() < timeoutTime) {
  wait(timeoutTime - System.currentTimeMillis());
  }
  这里会存在很小的可能性使得在做减法时,当前时间超过了超时的阈值,从而产生一个负值并传递给了wait(),进而会抛出一个IllegalArgumentException的异常。为了省得计算机一次罕见的线程切换,他引入了一个会偶尔发生并将不可思议地导致运算失败的bug。
  (2010年3月15日更新:Ajit Mandalay指出另外一个不好的细节:减法得到0,这意味着“无穷”,循环就有可能永远不会退出)
  你写的每一行代码都可能会有一个潜在的bug。所以,除非是当前立刻就需要的或者程序缺了就不能正常运行的,请不要写任何代码。不要推测性地写例程。如果不是立刻需要,就不要写抽象层。如果一个优化会增加任何的复杂性,哪怕是一个减法,也请抵制它。否则五年后,当你的代码中充满有可能是错误的而又从未真正需要的代码时,你会非常遗憾后悔的。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号