2011-05-28 113 views
13

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

因此,要总结,我的问题是:

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

  2. 什么是使用ExecutionAndPublication导致死锁初始化其他对象的一些可能的方法?

假设你有下面的代码:

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(); 
    } 
} 

回答

9
  1. 它作为记录 - 如果它不使用任何锁,这种用法不能造成任何死锁。
  2. 想象一下,您有一个通过从数据库中读取进行初始化的惰性值,但您希望确保只有一个线程随时访问数据库。如果您有其他访问数据库的代码,则可能会发生死锁。请看下面的代码:
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在这种情况下也在内部使用锁,所以会导致死锁。

在这种情况下,通过将访问移动到UpdateDb()以外的锁语句来访问lazyInt.Value可能很容易解决死锁问题,但在其他情况下可能并不那么微不足道(或显而易见)。

+1

伟大的答案@svick - 以相反的顺序获取的嵌套锁的经典示例,这是沿着我的想法 - 伟大的例子来澄清场景,谢谢! – dugas 2011-05-29 20:56:50