.net 2.0 锁定和 try-finally 之前的异常。除了线程中止之外还有其他异常吗?

.net 2.0 lock and exceptions before the try-finally. Are there any other exceptions besides thread abort?

今天我 运行 进入了这个: https://blogs.msdn.microsoft.com/ericlippert/2009/03/06/locks-and-exceptions-do-not-mix/

我使用的是 .net 2.0,所以,基本上,这段代码

lock(syncRootVar) {
    DoStuff();
}

会展开成这样

Monitor.Enter(syncRootVar);
try {
    DoStuff();
} finally {
    Monitor.Exit(syncRootVar);
}

正如 Lippert 在博客上所写的那样,在 Enter 调用和 try-finally 块之间可能存在 nop 操作,这是引发线程中止异常并因此弄乱锁的潜在位置。

我有两个问题:

正如文章所指出的,您似乎关心的问题已不再是问题。 C# 编译器已更改(并且 Roslyn 可能会保留更改)以便在 try/finally 内获取锁。无法获取锁但无法执行finally子句。

现在(同样如文章所指出的)您遇到了一个不同的问题:如果受保护代码块中的代码处于变异状态,则异常可能会导致其他代码看到 partially-mutated 状态。这可能是也可能不是问题;通常是这样,但当然每个特定场景都不同。在这种情况下,某些代码可能是安全的。


• Is there a common way of handling this troublesome situation and still clean up the lock object in order to not affect other threads?

对于您询问的具体情况,您可以做的最重要的两件事是:

  1. 不要中止线程。这始终是个好建议,应始终遵循。如果你不中止一个线程,你就不会有那个问题。
  2. 使用最新版本的编译器。较新版本的编译器不会生成易受此问题影响的代码。

• Are there other situations that might result in the lock being acquired, but exceptions raising before the try-finally block?

不,不是最新版本的编译器。连原来的情况都没有


现在那个讨厌的 "partially-mutated" 问题怎么办?好吧,您必须单独处理每个案例。但是,如果可能会抛出异常,并且可能会保留 partially-mutated 状态的锁,那么您将必须添加自己的 clean-up 代码。例如:

lock(syncRootVar) {
    try {
        DoStuff();
    } catch {
        UndoStuff();
        throw;
    }    
}