【问题标题】:Why does this code not end in a deadlock为什么这段代码不会以死锁结束
【发布时间】:2013-06-18 10:05:29
【问题描述】:

我有这个 C# 代码:

public class Locking
{

    private int Value1; private int Value2;

    private object lockValue = new Object();
    public int GetInt1(int value1, int value2)
    {
        lock (lockValue)
        {
            Value1 = value1;
            Value2 = value2;
            return GetResult();
        }

    }

    public int GetInt2(int value1, int value2)
    {
        lock (lockValue)
        {
            return GetInt1(value1, value2);
        }
    }

    private int GetResult()
    {
        return Value1 + Value2;
    }


}

因此,如果我执行GetInt2,基本上我预计会出现死锁,但代码只是执行。任何好的解释。

【问题讨论】:

    标签: c# .net c#-4.0 locking


    【解决方案1】:

    lock 阻塞正在执行的线程,除非该线程已经持有对象上的锁。

    在这种情况下,只有一个线程在执行;它在GetInt2 中锁定lockValue,然后进入GetInt1,在那里它再次遇到lockValue 上的锁定语句——它已经持有,所以它被允许继续。

    【讨论】:

    • 是 - 重要的一点是同一个线程可以进入它已经持有的任何锁,任意次数。
    【解决方案2】:

    C# 中的lock 语句是语法糖,编译器将其解释为对Monitor.Enter 的调用。这是documented(在“监视器”部分中)

    lock (x)
    {
        DoSomething();
    }
    

    等价于

    System.Object obj = (System.Object)x;
    System.Threading.Monitor.Enter(obj);
    try
    {
        DoSomething();
    }
    finally
    {
        System.Threading.Monitor.Exit(obj);
    }
    

    Monitor.Enter 的文档指出

    同一线程多次调用Enter是合法的 没有它阻塞;但是,必须有相同数量的 Exit 调用 在等待对象的其他线程解除阻塞之前调用。

    从上面很明显,只要只涉及一个线程,给定的代码就不会产生死锁。

    【讨论】:

      【解决方案3】:

      这里的一般情况是同步对象是否可重入。换句话说,如果它已经拥有锁,则可以被同一个线程再次获取。另一种说法是对象是否具有“线程亲和性”。

      在 .NET 中,Monitor 类(实现锁语句)、Mutex 和 ReaderWriterLock 是可重入的。 Semaphore 和 SemaphoreSlim 类是 not,您可以使用二进制信号量让您的代码死锁。实现锁定最便宜的方法是使用 Interlocked.CompareExchange(),它也不会是可重入的。

      使同步对象可重入需要额外的成本,它需要跟踪哪个线程拥有它以及在拥有线程上获取锁的频率。这需要存储 Thread.ManagedId 和一个计数器,两个整数。这影响了 C++ 中的选择,例如,C++11 语言规范最终将线程添加到标准库中。 std::mutex 类在该语言中可重入,并且添加递归版本的提议被拒绝。他们认为让它重入的开销太高了。可能有点笨拙,相对于调试意外死锁所花费的时间而言,成本是微不足道的 :) 但它是一种语言,在这种语言中,可以保证获取线程 ID 很便宜,就像在 .网。

      这在 ReaderWriterLockSlim 类中公开,您可以选择。请注意 RecursionPolicy 属性,它允许您在 NoRecursion 和 SupportsRecursion 之间进行选择。 NoRecursion 模式更便宜,而且真正苗条

      【讨论】:

      • 信号量按我的意图工作,这是我的代码 stackoverflow.com/a/17194374/98491 它只是 private static ss = new SemaphoreSlim(1, 1); 并且在代码中我将 ss.Wait();ss.Release() 包装到 try/finally 块中。
      • 很好的答案! +1 用于解释所有锁的工作原理,而不仅仅是询问的那个
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-08-20
      • 2012-08-27
      • 1970-01-01
      • 2021-12-10
      • 1970-01-01
      • 1970-01-01
      • 2021-06-20
      相关资源
      最近更新 更多