Try / Finally实际上是异常安全的吗?

问题描述

| 假设您有一段代码,例如:
resource = allocateResource();
try { /* dangerous code here  */ }
finally { free(resource); }
在这里没有提到任何特定的语言,但是我想Java,C#和C ++就是很好的例子(假设您在MSVC ++中使用
__try
/
__finally
)。 这是异常安全的吗? 我个人认为这不是异常安全的,因为如果在进入ѭ3enter块之前发生异常,该怎么办?然后,您的资源将泄漏。 不过,我已经看过足够多次了,所以我认为我缺少了一些东西……是吗?还是真的不安全? 编辑: 我并不是在问
allocateResource
引发异常,而是一种情况,在这种情况下,该函数返回之后但在分配
resource
之前得到了异常。     

解决方法

           我不是在问allocateResource抛出异常,而是一种情况   在那之后你得到一个例外   该函数已返回,但在分配资源之前。 尝试处理异常安全的这一方面非常麻烦,尤其是因为该语言结构不允许您在赋值语句的中间安装final处理程序。 我所有这些的基本原理是,如果您无法从函数调用的结尾到分配给变量,则您的系统已经处于崩溃状态。当您无法分配变量时,谁在乎您是否泄漏内存?     ,        关键是要在
try
块中包含所有可能引发异常的代码。在您的情况下:
try
{
    resource = allocateResource();
    //...
}
finally { free(resource); }
否则-不,当然不安全。     ,        对于C#,它被认为是不安全的,因为ThreadAbortException可以在资源分配和try块的开始之间抛出。因此,C#4更改了
using
块的扩展以在try内移动资源分配,而
finally
块使用了一个隐藏的布尔值(或对null进行测试,我记不清了)确定分配是否实际发生。     ,        这取决于allocateResource的编写方式。鉴于上面的allocateResource片段可以导致两个结果: 1)分配并返回资源 2)它除外(因此不返回资源) 因此,如果
allocateResource
肯定不会在抛出之前在内部泄漏任何分配,则上述内容也不会泄漏
resource
,因为该方法不能同时抛出和返回。     ,        只有try {}块中的代码是安全的。并且仅在正确捕获所有异常的情况下。 当然,该块外的代码将不会,而这正是所需的行为。 另请注意,finally {}块中的代码也可能引发异常,因此您可能需要在try或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块内,则将由finally处理 如果异常可能在分配之后和try / catch块之前发生,则应重新考虑代码,并将那些有问题的行移入该块内。     ,        我认为您正在回答自己的问题。如果
allocateResource
分配然后在将资源分配给变量之前引发异常,则资源将泄漏,
try/finally
块对此无能为力,因为大多数语言(包括Java和C#)中的
try/finally
块实际上并不了解资源你在他们里面使用。 因此,为了使ѭ14有效,ѭ4必须以某种方式保证是原子性的;要么毫无例外地分配并分配给变量,要么根本没有分配并失败。由于没有这样的保证(特别是考虑到不可预测的线程死亡),因此“ 14”个块不能有效地安全。 一些语言开始支持了解资源的
using
with
子句,因此能够安全地关闭它们(尽管这将取决于解释器/编译器/运行时的实现等)。     ,        在C#中,任何托管和未引用的资源都将在下一次运行时被垃圾回收,因此您在那里没有任何问题。