Lazy <t> ExecutionAndPublication - 可能导致死锁的示例</t>

时间:2011-05-28 21:25:11

标签: c# .net deadlock lazythreadsafetymode

LazyThreadSafetyMode的文档指出,如果初始化方法(或默认构造函数,如果没有初始化方法)在内部使用锁,则使用值ExecutionAndPublication会导致死锁。我试图更好地理解使用此值时可能导致死锁的示例。在我使用此值时,我正在初始化ChannelFactory。我看不到ChannelFactory的构造函数使用任何内部锁(使用Reflector查看类),所以我认为这种情况不适合可能的死锁情况,但我很好奇什么情况可能导致死锁以及是否有可能死锁初始化ChannelFactory。

总而言之,我的问题是:

  1. 是否可能导致使用ExecutionAndPublication初始化ChannelFactory的死锁?

  2. 使用ExecutionAndPublication导致死锁初始化其他对象有哪些可能的方法?

  3. 假设您有以下代码:

    class x
    {
       static Lazy<ChannelFactory<ISomeChannel>> lcf = 
            new Lazy<ChannelFactory<ISomeChannel>>(
            () => new ChannelFactory<ISomeChannel>("someEndPointConfig"), 
            LazyThreadSafetyMode.ExecutionAndPublication
            );
    
        public static ISomeChannel Create()
        {
            return lcf.Value.CreateChannel();
        }
    }
    

1 个答案:

答案 0 :(得分:10)

  1. 这是有记录的 - 如果它不使用任何锁,这种用法不会导致任何死锁。
  2. 想象一下,您有一个通过读取数据库初始化的惰性值,但是您希望确保任何时刻只有一个线程正在访问数据库。如果您有其他访问数据库的代码,则可能会出现死锁。请考虑以下代码:
  3. void Main()
    {
        Task otherThread = Task.Factory.StartNew(() => UpdateDb(43));
        Thread.Sleep(100);
        Console.WriteLine(lazyInt.Value);
    }
    
    static object l = new object();
    Lazy<int> lazyInt = new Lazy<int>(Init, LazyThreadSafetyMode.ExecutionAndPublication);
    
    static int Init()
    {
        lock(l)
        {
            return ReadFromDb();
        }
    }
    
    void UpdateDb(int newValue)
    {
        lock(l)
        {
            // to make sure deadlock occurs every time
            Thread.Sleep(1000);
    
            if (newValue != lazyInt.Value)
            {
                // some code that requires the lock
            }
        }
    }
    

    Init()从数据库读取,因此必须使用锁。 UpdateDb()写入数据库,因此它也需要锁定,并且由于Lazy在这种情况下也在内部使用锁,因此会导致死锁。

    在这种情况下,通过在锁定语句之外移动lazyInt.ValueUpdateDb()的访问权限来修复死锁很容易,但在其他情况下可能不那么简单(或显而易见)