Потокобезопасное ленивое создание экземпляров с использованием MEF - PullRequest
4 голосов
/ 08 января 2011
// 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 на volatile static int и регистрирую его где-нибудь).Проблема в том, что у меня нет доступа к определению Lazy, а MEF определяет, как класс Lazy создает объекты.Я должен заметить, что CompositionContainer имеет поточно-ориентированный параметр в конструкторе, который уже используется.

Мои вопросы:

1) Почему блокировка не работает?

2) Должен ли я использовать массив блокировок вместо одной блокировки для повышения производительности?

Ответы [ 2 ]

4 голосов
/ 08 января 2011

Является ли конструктор по умолчанию 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 не только для чтения, то вам также нужно синхронизировать доступ к этому экземпляру, в противном случае он может быть изменен в другом потоке, что приведет ккод упасть.

0 голосов
/ 08 января 2011

Для таких сценариев существует специальный конструктор Lazy<T, TMetadata>, где вы определяете LazyThreadSafetyMode при создании экземпляра Lazy ... В противном случае блокировка может не работать по многим различным причинам, например если это не единственное место, где когда-либо обращаются к свойству Value этого экземпляра Lazy<T>.

Кстати, вы получили опечатку в if заявлении ...

...