简体   繁体   English

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

[英]Thread-Safe lazy instantiating using MEF

// 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;
   }
}

Here I need synchronized access per loop. 在这里,我需要每个循环同步访问。 There are many threads iterating this loop and based on the key they are looking for, a lazy instance is created and returned. 有很多线程迭代这个循环,并根据它们正在寻找的key ,创建并返回一个惰性实例。

lazyObject should not be created more that one time. lazyObject应该lazyObject创建lazyObject Although Lazy class is for doing so and despite of the used lock, under high threading I have more than one instance created (I track this with a Interlocked.Increment on a volatile static int and log it somewhere). 虽然Lazy类是这样做的,尽管使用了锁,但在高线程下我创建了多个实例(我在一个volatile static int上使用Interlocked.Increment跟踪它并将其记录在某处)。 The problem is I don't have access to definition of Lazy and MEF defines how the Lazy class create objects. 问题是我无法访问Lazy定义,而MEF定义了Lazy类如何创建对象。 I should notice the CompositionContainer has a thread-safe option in constructor which is already used. 我应该注意到CompositionContainer在构造函数中有一个已经使用过的线程安全选项。

My questions: 我的问题:

1) Why the lock doesn't work ? 1)为什么锁不起作用?

2) Should I use an array of locks instead of one lock for performance improvement ? 2)我应该使用一系列锁而不是一个锁来提高性能吗?

Is the default constructor of T in your Lazy complex? Lazy复合体中T的默认构造函数吗? MEF uses LazyThreadSafetyMode.PublicationOnly which means each thread accessing the unitialised Lazy will generate a new() on T until the first to complete the initialisation. MEF使用LazyThreadSafetyMode.PublicationOnly ,这意味着访问单元化Lazy每个线程将在T上生成new() ,直到第一个完成初始化。 That value is then returned for all threads currently accessing .Value and their own new() instances are discarded. 然后为当前访问的所有线程返回该值.Value和它们自己的new()实例被丢弃。 If your constructor is complex (perhaps doing too much?) you should redefine it as doing minimal construction work and moving configuration to another method. 如果您的构造函数很复杂(可能做得太多了?),您应该将其重新定义为执行最少的构造工作并将配置移动到另一个方法。

You need to think about the method as a whole. 你需要考虑整个方法。 Should you consider: 你应该考虑:

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();
  }
}

You also need to consider that if plugins is not read-only then you need to synchronise access to that instance too, otherwise it may be modified on another thread, causing your code to fall over. 您还需要考虑,如果plugins不是只读的,那么您也需要同步对该实例的访问,否则可能会在另一个线程上修改,导致代码崩溃。

There is a specific constructor of Lazy<T, TMetadata> for such scenarios, where you define a LazyThreadSafetyMode when constructing a Lazy instance... Otherwise, the lock might not work for many different reasons, eg if this is not the only place where the Value property of this Lazy<T> instance is ever accessed. 对于这样的场景,有一个特定的Lazy<T, TMetadata>构造函数,您可以在构造Lazy实例时定义LazyThreadSafetyMode ...否则,锁定可能由于许多不同的原因而无法工作,例如,如果这不是唯一的地方访问此Lazy<T>实例的Value属性。

Btw you got I typo in the if statement... 顺便说一句,你在if声明中得到了错字......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM