这将是为什么即使使用资源尝试方法也可能存在这些警告的概念性答案。不幸的是,这也不是您可能希望实现的简单解决方案。
错误恢复无法失败
finally
对事务成功或失败之后执行的事务后控制流进行建模。
在失败的情况下,finally
即在执行捕获逻辑中从错误中恢复,它已经完全恢复之前的(之前我们到达我们的catch
目的地)。
想象一下,它呈现给遇到一个错误的概念问题,中央从错误中恢复的。
想象一下一个数据库服务器,我们试图在该数据库中提交事务,但是它在中途失败(例如,服务器在中间耗尽了内存)。现在服务器希望将事务回滚到某个点,好像什么也没发生。但是,可以想象它在回滚过程中遇到另一个错误。现在,我们最终对数据库进行了一半提交的事务-事务的原子性和不可分割的性质现在被破坏了,并且数据库的完整性也将受到损害。
这个概念性问题存在于处理错误的任何语言中,无论是使用C进行手动错误代码传播,还是使用C ++进行异常和析构函数,或者使用Java进行异常和finally
。
finally
不能以提供破坏者的方式相同的语言失败,析构函数在遇到异常的过程中也不能在C ++中失败。
避免此概念性难题的唯一方法是确保回滚事务和在中间释放资源的过程不可能遇到递归异常/错误。
因此,这里唯一安全的设计是writer.close()
不可能失败的设计。在设计中,通常有一些方法可以避免在恢复过程中此类事情可能失败而无法实现的情况。
不幸的是,这是唯一的方法-错误恢复不会失败。确保这一点的最简单方法是使那些“资源释放”和“反作用”功能无法失效。这并不容易-正确的错误恢复很难,而且不幸的是很难测试。但是,实现此目标的方法是确保“破坏”,“关闭”,“关闭”,“回滚”等任何功能都不会在此过程中遇到外部错误,因为此类功能通常需要在从现有错误中恢复的过程中被调用。
示例:记录
假设您想将内容记录在一个finally
块中。除非记录不能失败,否则这通常将是一个巨大的问题。日志记录几乎肯定会失败,因为它可能希望将更多数据附加到文件中,并且很容易找到失败的许多原因。
因此,这里的解决方案是使它成为可能,以便finally
块中使用的任何日志记录函数都不会抛出给调用者(它可能会失败,但不会抛出)。我们该怎么做?如果您的语言允许在有嵌套的try / catch块的情况下最终抛出,则这将是通过吞入异常并将其转换为错误代码避免抛出给调用者的一种方法,例如,也许可以单独记录日志可能会分别失败并且不在现有错误恢复堆栈展开的进程或线程。只要您可以与该进程进行通信而不会出现错误,那也是异常安全的,因为如果我们递归地从同一线程中抛出该安全问题,则仅在这种情况下存在安全问题。
在这种情况下,我们可以避免日志记录失败,但前提是它不会抛出日志,因为无法记录日志和不执行任何操作不是世界末日(例如,它不会泄漏任何资源或无法回滚副作用)。
无论如何,我相信您已经可以开始想象真正使软件具有异常安全性是多么的困难。除了最关键任务的软件外,可能没有必要在所有方面都做到这一点。但是值得一提的是如何真正实现异常安全,因为即使是非常通用的库作者也常常在这里摸索,破坏了使用该库的应用程序的整体异常安全。
SomeFileWriter
如果SomeFileWriter
可以扔进去close
,那么我会说它通常与异常处理不兼容,除非您从未尝试在涉及从现有异常中恢复的上下文中关闭它。如果该代码不在您的控制范围内,那么我们可能是SOL,但值得将此明显的异常安全问题通知作者。如果它在您的控制范围内,我的主要建议是确保关闭它不会因任何必要的方式而失败。
想象一下,如果操作系统实际上无法关闭文件。现在,任何试图在关闭时关闭文件的程序都将无法关闭。我们现在应该怎么做,只是保持应用程序处于打开状态并且处于混乱状态(可能没有),只是泄漏文件资源并忽略该问题(如果不是很关键就可以了)?最安全的设计:使其无法关闭文件是不可能的。