使用MEF进行线程安全的懒惰实例化

时间:2011-01-08 11:27:23

标签: c# multithreading locking mef lazy-evaluation

// Member Variable
private static readonly object _syncLock = new object();

// Now inside a static method

foreach (var lazyObject in plugins)
{
   if ((string)lazyObject.Metadata["key"] = "something")
   {
      lock (_syncLock)
      {
         // It seems the `IsValueCreated` is not up-to-date
         if (!lazyObject.IsValueCreated) 
            lazyObject.value.DoSomething();
      }
      return lazyObject.value;
   }
}

这里我需要每个循环同步访问。有许多线程在迭代这个循环并基于它们正在寻找的key,创建并返回一个惰性实例。

lazyObject不应该多次创建。虽然Lazy类是这样做的,尽管使用了锁,但在高线程下我创建了多个实例(我在Interlocked.Increment {{1}上使用volatile进行跟踪并将其记录在某个地方)。问题是我无法访问static int的定义,而Lazy定义了MEF类如何创建对象。我应该注意Lazy在构造函数中有一个已经使用的线程安全选项。

我的问题:

1)为什么锁不起作用?

2)我应该使用一系列锁而不是一把锁来提高性能吗?

2 个答案:

答案 0 :(得分:4)

T复合体中Lazy的默认构造函数是? MEF使用LazyThreadSafetyMode.PublicationOnly,这意味着访问单元化Lazy的每个线程将在new()上生成T,直到第一个完成初始化。然后为当前访问.Value的所有线程返回该值,并丢弃它们自己的new()实例。如果您的构造函数很复杂(可能做得太多了?),您应该将其重新定义为最少的构造工作并将配置移动到另一个方法。

您需要考虑整个方法。你应该考虑:

public IPlugin GetPlugin(string key)
{
  mutex.WaitOne();

  try
  {
    var plugin = plugins
      .Where(l => l.Metadata["key"] == key)
      .Select(l => l.Value);
      .FirstOrDefault();

    return plugin;
  }
  finally
  {
    mutex.ReleaseMutex();
  }
}

您还需要考虑如果plugins不是只读的,那么您也需要同步对该实例的访问权限,否则可能会在另一个线程上进行修改,从而导致代码崩溃。

答案 1 :(得分:0)

对于这种情况,有Lazy<T, TMetadata>的特定构造函数,在构造Lazy实例时定义LazyThreadSafetyMode ...否则,锁定可能由于许多不同的原因而无法工作,例如如果这不是唯一访问此Value实例的Lazy<T>属性的地方。

顺便说一句,你在if陈述中得到了错字......