【问题标题】:C# Lazy Initialization && Race-to-initialize?C# 延迟初始化 && 竞速初始化?
【发布时间】:2012-07-19 07:20:17
【问题描述】:

readingLazyInitializer 之后是:

它提供了另一种具有多个线程的初始化模式 竞相初始化。

这是一个示例:

Expensive _expensive;
public Expensive Expensive
     {
       get // Implement double-checked locking
         {
           LazyInitializer.EnsureInitialized (ref _expensive,() => new Expensive());
           return _expensive;
         }
     }

问题 #1

看:

为什么#A 说它实现了双重检查锁定?它只是一个获得财产?

问题 #2

#B(lambda 表达式)是线程安全的吗?

问题 #3

所以我通过查看样本搜索了这个“race-to-initialize”

volatile Expensive _expensive;
public Expensive Expensive
{
  get
  {
    if (_expensive == null)
    {
      var instance = new Expensive();
      Interlocked.CompareExchange (ref _expensive, instance, null);
    }
    return _expensive;
  }
}

然后我想:初始化的竞赛不是线程安全的吗?

e/g/ 如果有 2 个线程进入:

昂贵的对象将被创建两次!

再说一遍,3个问题

1)为什么#A 说它实现了双重检查锁定?它只是一个获得财产?

2)#B(lambda 表达式)是线程安全的吗?

3) 初始化竞争不是线程安全的

【问题讨论】:

  • 评论“//实施...”听起来更像是对未来的计划,我想说

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


【解决方案1】:

EnsureInitialized 有多种重载。有些接受synclock 对象(可以是null,将由EnsureInitialized 方法创建)。其他人没有synclock 作为参数。所有EnsureInitialized 保证如果在对象未初始化时由两个(或更多)不同的线程同时调用,则两个线程将收到对同一对象的引用。所以:

Expensive _expensive;

// On thread 1
LazyInitializer.EnsureInitialized (ref _expensive,() => new Expensive());

// On thread 2
LazyInitializer.EnsureInitialized (ref _expensive,() => new Expensive());

两个线程将看到的_expensive 对象将是相同的。

唯一的问题是new Expensive() 可以被调用两次(每个线程一次,所以在多线程竞争中它可以被调用更多次。)

如果您不想要它,请使用 synclock 重载:

Expensive _expensive;
object _sync = null;
bool _useless;

// On thread 1
LazyInitializer.EnsureInitialized (ref _expensive, ref useless, ref _sync, () => new Expensive());

// On thread 2
LazyInitializer.EnsureInitialized (ref _expensive, ref useless, ref _sync, () => new Expensive());

现在new Expensive() 将只被调用一次,对于两个(或更多)正在运行的线程的每个可能的组合。

【讨论】:

  • race-to-initializerace condition 吗? (是同一个词吗?)
  • @RoyiNamir 这取决于初始化程序是否有副作用 :-) 如果它没有副作用,那么它就不是真正的比赛,因为(取自 wiki)A race condition or race hazard is a flaw in an electronic system or process whereby the output or result of the process is unexpectedly and critically dependent on the sequence or timing of other events.没有副作用 => 输出没有差异(很明显,资源的大量使用可以归类为副作用)。如果方法是new List<int>(),则没有任何问题。如果是transfer 100$ to someone,那就有。
  • 那么我使用LazyInitializer.EnsureInitialized(没有syncobj)有什么好处?我可以使用Lazy<Expensive> _expensive = new Lazy<Expensive> (() => new Expensive(), true);,它也是 Thread sagfe && lazy...
  • @RoyiNamir 受益于什么?直接使用CompareExchange?您不必理解它背后的魔力,并且您(或与您在相同代码上工作的多线程方面不太懂的人)无法破坏它。它是正确实现的模式。
  • (也许我没听懂你的意思)但是请看问题#3 的代码。我写了这段代码来理解什么是初始化竞赛(仍然不明白)。它也在那里使用:Interlocked.CompareExchange 而不是LazyInitializer.EnsureInitialized 所以?你能帮我解决一下吗?
猜你喜欢
  • 2020-05-10
  • 1970-01-01
  • 2011-11-17
  • 1970-01-01
  • 2020-10-08
  • 2017-11-11
  • 1970-01-01
  • 2014-08-23
  • 2011-12-19
相关资源
最近更新 更多