обновляемая блокировка чтения в c # - PullRequest
3 голосов
/ 27 сентября 2011

У меня есть словарь, который распределяется между несколькими потоками. Каждый поток читает определенное значение из словаря в соответствии с заданным ключом, но - если ключ не существует в словаре, поток должен добавить его в словарь.
Чтобы решить проблему с синхронизацией, я использую класс ReaderWriterLockSlim, который в основном дает мне синхронизацию блокировки чтения-записи (то есть чтения могут выполняться параллельно, но только по одной записи одновременно), но добавляет опцию обновления для читателя. Используя опцию обновления, я могу проверить, есть ли данный ключ в словаре, а если нет - обновить блокировку и записать ее, обещая только одно дополнение для каждого ключа.

Моя проблема в том, что я не могу создать две обновляемые блокировки одновременно, что означает, что это решение бесполезно ...: (

Может кто-нибудь объяснить мне, почему Microsoft решила внедрить обновляемую блокировку таким образом (что я не могу иметь более одной обновляемой блокировки одновременно ...), и дать мне любую идею, как я могу реализовать обновляемую блокировку с помощью сам \ дать мне еще одну идею для синхронизации моего общего словаря?

Ответы [ 4 ]

8 голосов
/ 27 сентября 2011

Если вы используете .NET 4.0, почему бы не использовать ConcurrentDictionary

4 голосов
/ 27 сентября 2011

Понятия не имею, почему ReaderWriterLockSlim был реализован таким образом. Я подозреваю, что есть веские причины.

Почему бы просто не использовать ConcurrentDictionary ? Тогда вам не нужно беспокоиться о явной блокировке.

Тем не менее, я не вижу, где наличие нескольких обновляемых блокировок считывателя могло бы помочь вам. Рассмотрим следующий сценарий:

Thread1 enters the lock in upgradeable mode
Thread2 enters the lock in upgradeable mode
Thread1 searches for "xyzzy" and doesn't find it
Thread2 searches for "xyzzy" and doesn't find it
Thread2 upgrades to a write lock
Thread1 waits to upgrade to a write lock
Thread2 updates and releases the lock
Thread1 acquires the write lock and overwrites what Thread2 had written

Чтобы не допустить перезаписи Thread1 того, что сделал Thread2, вам нужно написать следующую логику:

Enter upgradable read lock
if (!dict.TryGetValue(...))
{
    Enter write lock
    if (!dict.TryGetValue(...))  // extra check required!
    {
    }
}

Что именно и нужно делать, если нет обновляемых блокировок.

1 голос
/ 27 сентября 2011

UpgradeableReadLock существует, поэтому вам не нужно снимать блокировку чтения до получения блокировки записи.Это позволяет вам сделать это:

   locker.EnterUpgradeableReadLock();
   ...
   locker.EnterWriteLock(); 
   ...

вместо

   locker.EnterReadLock();
   ...
   locker.ExitReadLock();
   locker.EnterWriteLock();
   ...

Поскольку это блокировка ReaderWriter, у вас все равно может быть только один модуль записи в любой момент времени, который можно обновить с помощью блокировки чтениянавязывает.Это означает, что эти два не эквивалентны;первая позволяет другим абонентам проникнуть внутрь, но разрешает параллелизм в части чтения.

Если у вас есть случаи, когда вы можете использовать ReadLocks для большинства операций чтения и UpgradeableRead для обновления / вставки данных, это и есть намерение.Если все ваши доступы к данным являются потенциальными авторами записи, то это, вероятно, не работает так же хорошо, и вы можете использовать простую блокировку (объект) вокруг ваших записей для обеспечения монопольного доступа при добавлении / обновлении.

0 голосов
/ 27 сентября 2011

Не похоже, что вы используете оптимальное решение этой проблемы.

Пример:

  protected static object _lockObj = new object();

  if(_dictionary.ContainsKey(key))
  {
      return _dictionary[key];
  }
  else
  {
      lock(_lockObj)
      {
           if(_dictionary.ContainsKey(key))
              return _dictionary[key];

           _dictionary.Add(key, "someValue");
           return "someValue";
      }
  }

Если вы используете .NET 4, попробуйте использовать класс ConcurrentDictionary, о котором упоминали другие.

...