Thread-Safe lazy instantiating using MEF

血红的双手。 提交于 2019-12-13 13:18:32

问题


// 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.

lazyObject should not be created more that one time. 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). The problem is I don't have access to definition of Lazy and MEF defines how the Lazy class create objects. I should notice the CompositionContainer has a thread-safe option in constructor which is already used.

My questions:

1) Why the lock doesn't work ?

2) Should I use an array of locks instead of one lock for performance improvement ?


回答1:


Is the default constructor of T in your Lazy complex? 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. That value is then returned for all threads currently accessing .Value and their own new() instances are discarded. 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.




回答2:


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, e.g. if this is not the only place where the Value property of this Lazy<T> instance is ever accessed.

Btw you got I typo in the if statement...



来源:https://stackoverflow.com/questions/4633634/thread-safe-lazy-instantiating-using-mef

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!