Try/Finally实际上是异常安全的
本文关键字:异常 安全 实际上 Finally Try | 更新日期: 2023-09-27 17:58:19
假设您有一段代码,如:
resource = allocateResource();
try { /* dangerous code here */ }
finally { free(resource); }
我在这里不是指任何特定的语言,但我想Java、C#和C++将是很好的例子(假设您在MSVC++中使用__try
/__finally
)。
这个例外安全吗
就我个人而言,我不认为这是异常安全的,因为如果在进入try
块之前出现异常怎么办?然后你的资源就会泄漏。
不过,我已经看了足够多次了,我想我错过了一些东西。。。是吗?或者这真的不安全吗?
编辑:
我不是在问allocateResource
抛出异常,而是在函数返回之后,但在分配resource
之前,出现异常的情况。
我问的不是allocateResource抛出异常,而是一种情况其中在该函数已返回,但在分配资源之前。
尝试处理异常安全的这一方面会变得非常混乱,尤其是因为语言构造不允许在赋值语句的中间安装finally处理程序。
我对这一切的基本原理是,如果你不能从函数调用的末尾开始为变量赋值,那么你的系统就已经被冲洗掉了。当你不能分配给一个变量时,谁在乎你是否泄露了内存?
重点是让所有可以在try
块内抛出异常的代码。在您的情况下:
try
{
resource = allocateResource();
//...
}
finally { free(resource); }
否则——不,当然不安全。
在C#的情况下,它被认为是不安全的,因为可以在资源分配和try块的开头之间抛出ThreadAbortException。出于这个原因,C#4更改了using
块的扩展,以在try中移动资源分配,而finally
块使用隐藏的布尔值(或对null进行测试——我记不清了)来确定分配是否真的发生了。
这取决于allocateResource的编写方式。给定上面的片段,allocateResource可能会导致两种结果:1) 它分配并返回资源2) 它超出(因此不返回资源)
因此,如果allocateResource
在抛出之前确定不会在内部泄露任何分配,那么上述方法不会泄露resource
,因为该方法不能同时抛出和返回。
只有try{}块内的代码是安全的。并且只有在所有异常都被正确捕获的情况下。
当然,块外的代码不会,这正是所需的行为。
请注意,finally{}块中的代码也可能引发异常,因此您可能需要在finally或catch块中包含try-catch块。
例如:
try {
// your code here
} finally {
try {
// if the code in finally can throw another exception, you need to catch it inside it
} catch (Exception e) {
// probably not much to do besides telling why it failed
}
} catch (Exception e) {
try {
// your error handling routine here
} catch (Exception e) {
// probably not much to do besides telling why it failed
}
}
- 如果异常在分配之前抛出,则没有任何可释放的内容,因此没有任何可泄漏的内容
- 如果异常发生在分配之后的和try/catch块内部的<em],则将由finally处理>
- 如果异常可能发生在分配之后和try/catch块之前,则应重新考虑代码,并将那些有问题的行移到块内
我认为你在回答自己的问题。如果allocateResource
在资源被分配给变量之前分配并抛出异常,那么资源就会泄漏,并且try/finally
块对此无能为力,因为大多数语言(包括Java和C#)中的try/finally
块实际上并不知道您在其中使用的资源。
因此,为了使try/finally
有效,allocateResource
必须以某种方式保证是原子;要么分配并赋值给变量而不失败,要么根本不分配而失败。由于没有这样的保证(特别是考虑到不可预测的线程死亡),因此try/finally
块不能有效地安全。
一些语言开始支持using
或with
子句,知道关于资源的信息,因此能够安全地关闭它们(尽管这取决于解释器/编译器/运行时等的实现)
在C#中,任何托管和未引用的资源都将在下一次运行中被垃圾收集,因此您没有问题。