当加载失败时,Threadsafe延迟加载

时间:2010-08-09 08:49:03

标签: c# .net lazy-loading thread-safety double-checked-locking

我花了大约一个小时寻找关于我想要完成的事情的一致意见,但尚未在特定方向找到任何结论。

我的情况如下:

  • 我有一个多线程应用程序(.NET Web服务)
  • 我的类使用的对象加载时间不可忽略,所以我想将它们作为静态类成员维护
  • 间歇性构造这些对象的代码失败的可能性很小

我以前使用的方法是在静态构造函数中构造这些对象。这样做的问题在于,如上所述,构造函数偶尔会失败,并且一旦.NET静态构造函数失败,整个类就被清除,直到重新启动进程。这种方法没有第二次机会。

此后最直观的方法是使用双重检查锁定。有很多关于双重检查锁定的弊端并且说使用静态构造函数的页面,我已经在做了,但这对我来说似乎不是一个选项,因为静态构造函数有可能会失败并使整个班级失望。

我正在考虑使用的实现(当然简化)如下。所有的类和成员名称都是纯粹的示范,而不是我实际使用的。这种方法会有问题吗?有谁能建议更好的方法?

public class LazyMembers
{
    private static volatile XmlDocument s_doc;
    private static volatile XmlNamespaceManager s_nsmgr;
    private static readonly object s_lock = new object();

    private static void EnsureStaticMembers()
    {
        if (s_doc == null || s_nsmgr == null)
        {
            lock (s_lock)
            {
                if (s_doc == null || s_nsmgr == null)
                {
                    // The following method might fail
                    // with an exception, but if it succeeds,
                    // s_doc and s_nsmgr will be initialized
                    s_doc = LoadDoc(out s_nsmgr);
                }
            }
        }
    }

    public XmlNamespaceManager NamespaceManager
    {
        get
        {
            EnsureStaticMembers();
            return s_nsmgr;
        }
    }

    public XmlDocument GetDocClone()
    {
        EnsureStaticMembers();
        return (XmlDocument)s_doc.Clone();
    }
}

1 个答案:

答案 0 :(得分:2)

如果使用.NET 4.0,可以参考Lazy<T>LazyThreadSafetyMode(这取决于您是否希望在多线程环境中创建或不创建T的实例。在您的情况下,您需要参考Lazy<T>(Func<T> func, LazyThreadSafetyMode)构造函数 - here(MSDN)

否则(如果使用3.5或更低版本),CAS技术可以创建单个实例而无需锁定。

这样的事情:

get {
    if( _instance == null) {
        var singleton = new Singleton();
        if(Interlocked.CompareExchange(ref _instance, singleton, null) != null) {
            if (singleton is IDisposable) singleton.Dispose();
        }
    }
    return _instance;
}

但是,在这里,您只能实现LazyThreadSafetyMode.Publications行为 - 其他线程只能看到一个实例,但很少可以创建。

此外,在代码中对null进行双重检查应该没有任何问题 - 它在.NET世界中是安全的(至少在x86机器和相关的内存模型上)。在2004年之前,Java世界存在一些问题,AFAIK。