C#Lazy <t>&amp;种族到初始化?</T>

时间:2012-12-02 05:20:01

标签: c# .net multithreading .net-4.0 lazy-evaluation

在询问this related question 后,我仍有疑问。

Lazy<T>的整个想法(AFAIK)是在我们需要时创建对象 。 为什么?因为创建它很昂贵。

最后我想要的是Expensive对象将被创建>1次。

我不在乎许多线程最终会产生相同的引用。我只是不希望他们创建多个实例。

所以Lazyinitializer通过syncLock处理此问题:

LazyInitializer.EnsureInitialized (ref _expensive, ref useless, ref _sync, () => new Expensive());

但是Lazy<T>如何处理呢? 我在msdn中搜索过,无法找到任何syncLock重载...

我错过了什么?

2 个答案:

答案 0 :(得分:4)

你在问懒惰是如何在内部工作的吗?根据{{​​3}}:

,Lazy确保只创建一个
  

默认情况下,Lazy对象是线程安全的。也就是说,如果构造函数   没有指定线程安全的种类,Lazy对象呢   创建是线程安全的。在多线程场景中,第一个线程   访问线程安全的Lazy对象的Value属性初始化   它适用于所有线程上的所有后续访问,并且所有线程共享   相同的数据。因此,哪个线程初始化无关紧要   对象和竞争条件是良性的。

如果您实际上询问它是如何在内部工作的,那么它似乎正在使用某种lock

        object obj = Volatile.Read<object>(ref this.m_threadSafeObj);
        bool flag = false;
        try
        {
            if (obj != Lazy<T>.ALREADY_INVOKED_SENTINEL)
            {
                Monitor.Enter(obj, ref flag);
            }
            if (this.m_boxed == null)
            {
                boxed = this.CreateValue();
                this.m_boxed = boxed;
                Volatile.Write<object>(ref this.m_threadSafeObj, Lazy<T>.ALREADY_INVOKED_SENTINEL);
            }
            else
            {
                boxed = (this.m_boxed as Lazy<T>.Boxed);
                if (boxed == null)
                {
                    Lazy<T>.LazyInternalExceptionHolder lazyInternalExceptionHolder = this.m_boxed as Lazy<T>.LazyInternalExceptionHolder;
                    lazyInternalExceptionHolder.m_edi.Throw();
                }
            }
        }
        finally
        {
            if (flag)
            {
                Monitor.Exit(obj);
            }
        }

注意Monitor.Enter和Monitor.Exit调用。

答案 1 :(得分:3)

听起来你想要查看带有LazyThreadSafetyMode的构造函数重载。

Lazy<T> lazy = new Lazy<T>(() => new T, LazyThreadSafetyMode.ExecutionAndPublication);

Lazy<T>基本上是LazyInitializer的用户友好版本,单线程或多线程init的确切实现隐藏在该枚举后面。