1

假设您有一段代码,例如:

resource = allocateResource();
try { /* dangerous code here  */ }
finally { free(resource); }

我在这里不是指任何特定的语言,但我想 Java、C# 和 C++ 将是很好的例子(假设你在 MSVC++ 中使用__try/ )。__finally

这是异常安全的吗?

就个人而言,我不认为这是异常安全的,因为如果在进入块之前出现异常怎么办?try然后你的资源就会泄露。

不过,我已经看过足够多的时间了,我认为我错过了一些东西……是吗?或者这真的不安全吗?


编辑:

不是在询问是否allocateResource抛出异常,而是在该函数返回之后但分配之前 resource得到异常的情况。

4

7 回答 7

6

我不是在询问 allocateResource 是否引发异常,而是在该函数返回之后但在分配资源之前获得异常的情况。

尝试处理异常安全的这一方面变得非常混乱,尤其是因为语言结构不允许您在赋值语句中间安装 finally 处理程序。

我对这一切的理由是,如果你不能从函数调用结束到分配给一个变量,那么你的系统已经被水洗了。当您无法分配给变量时,谁在乎您是否泄漏内存?

于 2011-06-04T05:15:27.820 回答
3

关键是让所有可以在块内引发异常的代码。try在你的情况下:

try
{
    resource = allocateResource();
    //...
}
finally { free(resource); }

否则 - 不,当然它不安全。

于 2011-06-04T04:58:16.417 回答
2

在 C# 的情况下,它被认为是不安全的,因为在资源分配和 try 块开始之间可能会引发 ThreadAbortException。出于这个原因,C#4 更改了using块的扩展以将资源分配移动到 try 中,并且finally块使用隐藏的布尔值(或针对 null 的测试——我不记得确切)来确定分配是否实际发生.

于 2011-06-04T05:10:10.500 回答
1

这取决于 allocateResource 的编写方式。鉴于上面 allocateResource 的代码片段可能导致两种结果:1)它分配并返回资源 2)它排除(因此不返回资源)

因此,如果allocateResource确保在抛出之前不会在内部泄漏任何分配,则上述内容不会泄漏resource,因为该方法不能同时抛出和返回。

于 2011-06-04T05:02:39.473 回答
0

只有 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 块内,则由 finally 处理
  • 如果异常可能分配之后和try/catch 块之前发生,则应重新考虑代码并将那些有问题的行移到块内。
于 2011-06-04T05:02:05.893 回答
0

我认为您正在回答自己的问题。如果allocateResource在将资源分配给变量之前分配然后抛出异常,则资源将泄漏,并且没有任何try/finally块可以解决它,因为try/finally大多数语言(包括 Java 和 C#)中的块实际上并不知道您在内部使用的资源他们。

因此,为了try/finally有效allocateResource,必须以某种方式保证是原子的;要么分配并分配给变量而不会失败,要么根本不分配并且失败。由于没有这样的保证(特别是考虑到不可预测的线程死亡),那么try/finally块就不能有效地安全。

一些语言开始支持usingwith子句了解资源,因此能够安全地关闭它们(尽管这将取决于解释器/编译器/运行时等的实现)

于 2011-06-04T05:22:47.483 回答
0

在 C# 中,任何托管和未引用的资源都将在下次运行期间被垃圾收集,因此您没有问题。

于 2011-06-04T05:31:26.350 回答