3

在问了这个相关问题 之后,我还有一个问题。

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

我想要的最后一Expensive件事是该对象将被创建>1时间。

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

所以Lazyinitializer通过以下方式处理syncLock

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

可是怎么Lazy<T>办呢?我在msdn中搜索过,找不到任何syncLock超载...

我错过了什么?

4

2 回答 2

4

您是在问 Lazy 在内部是如何工作的吗?根据MSDN 文档,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 调用。

于 2012-12-02T05:35:10.073 回答
3

听起来您想查看采用LazyThreadSafetyMode.

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

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

于 2012-12-02T05:33:30.787 回答