代码之家  ›  专栏  ›  技术社区  ›  Kirlim

.net 2.0锁定和异常。除了线程中止之外,还有其他异常吗?

  •  0
  • Kirlim  · 技术社区  · 9 年前

    今天我遇到了这样的情况: 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操作,这是引发线程中止异常的潜在位置,因此会破坏锁。

    我对此有两个问题:

    • 有没有一种常见的方法来处理这种麻烦的情况,并仍然清理锁对象,以避免影响其他线程?
    • 是否存在其他可能导致锁被获取,但在try-finally块之前引发异常的情况?
    1 回复  |  直到 9 年前
        1
  •  0
  •   Peter Duniho    9 年前

    正如文章所指出的,你似乎关心的问题不再是一个问题。C#编译器已被更改(大概Roslyn会保留更改),因此锁将在 try / finally 。无法获取锁,但无法执行 最后 条款

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


    有没有一种常见的方法来处理这种麻烦的情况,并仍然清理锁对象,以避免影响其他线程?

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

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

    是否存在其他可能导致锁被获取,但在try-finally块之前引发异常的情况?

    不,不适用于最新版本的编译器。甚至没有原来的情况。


    现在,那个令人讨厌的“部分变异”问题又如何呢?好吧,你必须单独处理每一个案例。但是,如果可能引发异常,并且可能使锁处于部分变异状态,那么您必须添加自己的清理代码。例如。:

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