【问题标题】:Lazy<T> ExecutionAndPublication - Examples That Could Cause DeadlockLazy<T> ExecutionAndPublication - 可能导致死锁的示例
【发布时间】:2011-05-28 21:25:11
【问题描述】:

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

所以,总而言之,我的问题是:

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

【问题讨论】:

    标签: c# .net deadlock lazythreadsafetymode


    【解决方案1】:
    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 的访问移到lock 语句之外来修复死锁,但在其他情况下可能不是那么简单(或明显)。

    【讨论】:

    • 很好的答案@svick - 嵌套锁以相反顺序获取的经典示例,这与我的想法一致 - 很好的示例来阐明场景,谢谢!
    • 也许我不明白,但这段代码在本质上似乎有点理论化。很难知道可能在构造函数中调用的第三方或系统库代码是否在内部使用了锁,并且在使用默认的ExecutionAndPublication 模式时需要非常小心。但是,在使用 Lazy 时,针对这种特定情况和实际多线程死锁情况的解决方案是改用 PublicationOnly 模式。有什么已知的缺点吗?为什么不是默认的?
    • @horiac7 问题不只是任何锁,它必须是一个可以与Lazy&lt;T&gt; 锁进入循环的锁。而且在设计良好的库中,这种锁往往很少见,以避免出现死锁的可能性。所以ExecutionAndPublication 还不错。而PublicationOnly表示初始化代码可以执行多次,这可能是意料之外的。
    猜你喜欢
    • 1970-01-01
    • 2021-09-18
    • 1970-01-01
    • 1970-01-01
    • 2018-04-20
    • 2012-09-20
    相关资源
    最近更新 更多