使用“捕获异常”来提高可读性,好还是不好?


9

The Pragmatic Programmer何时使用异常小节中,该书代替了以下内容:

retcode = OK;
     if (socket.read(name) != OK) {
      retcode = BAD_READ;
    }
     else {
      processName(name);
       if (socket.read(address) != OK) {
        retcode = BAD_READ;
      }
       else {
        processAddress(address);
         if (socket.read(telNo) != OK) {
          retcode = BAD_READ;
        }
         else {
          //  etc, etc...
        }
      }
    }
     return retcode;

, 他们更喜欢:

 retcode = OK;
     try {
      socket.read(name);
      process(name);
      socket.read(address);
      processAddress(address);
      socket.read(telNo);
      //  etc, etc...
    }
     catch (IOException e) {
      retcode = BAD_READ;
      Logger.log( "Error reading individual: " + e.getMessage());
    }
     return retcode;

仅仅是因为它看起来更整洁。我全都喜欢整洁的代码,但是,不是不必要地捕获异常是性能瓶颈吗?

我可以理解,我们应该放弃对更整洁的代码的细微优化(至少99%的时间),但是据我所知,捕获异常属于代码类,在运行时会有明显的延迟。因此,我想知道为什么第二段代码比第一段代码更可取?

或者,您更喜欢哪种代码?


从Code Review迁移而来,因为它是一个最佳实践问题,而不是Code Review问题
Winston Ewert

1
IMO,如果您希望能够从套接字读取这些值,那么IOEx 就是例外。
史蒂文·埃弗斯


你测量了吗?

@ThorbjørnRavnAndersen当然是在看书,没有真正的“测试用例”来衡量..但是,如果我们使用人为的代码来衡量它,那么显然会有性能上的损失
Pacerier

Answers:


18

通常,异常只有在实际抛出时才会变慢。通常,这种情况下的例外情况很少见,因此您不必担心。您仅应真正担心异常的性能,如果它们一直在发生,因此是一个重要因素。

第二个代码更好,因为它更容易遵循逻辑。错误处理已很好地本地化。唯一的异议是,如果您使用例外,则使用例外。不要捕获异常,然后返回错误代码。


4
同意,我不希望看到带有例外的语言的错误代码。大多数时候,您应该捕获,记录和重新抛出而不中断调用堆栈。在大多数情况下,“例外”情况是否较慢并不重要。该应用程序有问题,请花点时间正确处理。只要异常是例外情况,并且不是应用程序的正常操作或愉快路径的一部分,通常就不会产生代码异味。
CaffGeek

顺便说一句,我想知道如何证明EStreamException正确,DataStreams使用它来检测行尾(行尾不是例外),如docs.oracle.com/javase/tutorial/essential/io/datastreams.html中所示
Pacerier,2011年

@Pacerier,这可能是例外。该行中的输入用完可能表明数据错误或出现严重错误。我怀疑预期用途是解析一行中的几个字段。
温斯顿·埃韦特

@Downvoter,如果您对答案有疑问,请解释!
温斯顿·埃韦特

5

好吧,正如他们也说的那样,异常应该处理例外情况,并且由于这是例外情况(即发生在很少之间的事情),我想说这也适用于此。

另外,我建议不要对这样的事情进行微优化。更多地关注代码的可读性,因为与编写新代码相比,您花费更多的时间阅读代码。

要真正确保这是一个性能瓶颈,请进行一些性能分析,然后基于该分析来分析并关注于更关键的任务。


5

我可以理解,我们应该放弃对更整洁的代码的细微优化(至少99%的时间),但是据我所知,捕获异常属于代码类,在运行时会有明显的延迟。

您从哪里知道?您如何定义“明显的延迟”?

这恰恰是那种导致无用的过早优化的模糊(完全错误)的性能神话。

与将两个数字相加相比,抛出和捕获异常可能会比较慢,但是与从套接字读取数据相比,这完全没有意义。在读取单个网络数据包的同时,您可能会抛出并捕获一千个异常。而且我们在这里不是在谈论成千上万的例外,只有一个例外。


我来自.NET所以原谅我的Java知识,但我亲身经历捕获异常在.NET造成该固定除去疯狂的延迟(秒量级)“捕获的异常代码” ...
Pacerier

@Pacerier,认真吗?秒级?我想也许正在捕获很多例外?然后我可以看到。
温斯顿·埃韦特

3
@Pacerier:如果异常导致了疯狂的延迟,显然造成异常的原因并不罕见,因此应以其他方式处理。任何系统都无需花费几秒钟来处理异常,我认为微软有足够的能力避免这种情况。
David Thornley

5

我同意您已经说过的例外情况,因此使用异常处理是合理的。

为了更直接地解决您的性能问题,我认为您需要保持对事情的了解。在这种情况下引发/捕获异常可能需要一个微秒的数量级。随着流控制的发展,它的速度很慢 -比普通if语句许多倍,对此毫无疑问。

同时,请记住,您在这里所做的是从网络读取数据。以每秒10兆位的速度(对于本地网络来说很慢,但是随着Internet连接的发展很快),这与读取一个字节信息的时间相同。

当然,仅当您实际抛出异常时才产生开销-当未引发异常时,通常根本没有开销或根本没有开销(至少从我所看到的来看,最重要的开销不是来自异常异常处理本身,例如添加更多潜在的控制流,使代码也更难于编译器进行优化)。

在这种情况下,当引发异常时,会将数据写入日志。鉴于日志的性质,很有可能您在写入后立即刷新该输出(以确保不会丢失)。再次写入磁盘是一个很慢的操作-您需要相当快的企业级SSD才能达到每秒数万次IOP的范围。用于记录日志的磁盘可能很容易被限制为每秒数百个IOP。

底线:在某些情况下,异常处理开销可能会很大,但这几乎可以肯定不是其中一种。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.