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

时间:2016-01-15 01:35:46

标签: c# multithreading locking .net-2.0

今天我遇到了这个问题: 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 个答案:

答案 0 :(得分:0)

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

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


  

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

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

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

    •是否存在可能导致获取锁定的其他情况,但是在try-finally阻止之前出现异常?

    不,不是最新版本的编译器。甚至没有最初的情况。


    那个讨厌的“部分变异”问题呢?那么,你必须单独处理每个案例。但是如果可能抛出异常,并且可以将锁保留为部分变异状态,则必须添加自己的清理代码。 E.g:

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