Расширение ReaderWriterLockSlim с действием - PullRequest
2 голосов
/ 13 сентября 2011

Рассмотрим следующие расширения:

public static class ReaderWriteExt
{
    public static void ExecWriteAction(this ReaderWriterLockSlim rwlock, Action action)
    {
        rwlock.EnterWriteLock();
        try
        {
            action();
        }
        finally
        {
            rwlock.ExitWriteLock();
        }
    }
    public static void ExecUpgradeableReadAction(this ReaderWriterLockSlim rwlock, Action action)
    {
        rwlock.EnterUpgradeableReadLock();
        try
        {
            action();
        }
        finally
        {
            rwlock.ExitUpgradeableReadLock();
        }
    }
}

Также рассмотрим следующий пример использования (без некоторого вспомогательного кода):

private static ReaderWriterLockSlim _rwlock = new ReaderWriterLockSlim();
private static ... _cacheEntries = ....;

public static void RemoveEntry(string name)
{
    WeakReference outValue = null;
    _rwlock.ExecUpgradeableReadAction(() =>
        {                    
            if (_cacheEntries.TryGetValue(name, out outValue))
            {
                if (!outValue.IsAlive)
                {
                    _rwlock.ExecWriteAction(() => _cacheEntries.Remove(name));
                }
            }
        });
}

Я новичок в C # и не смог найти достаточно информации по этим темам, которая могла бы мне помочь. На мой вопрос: я рассматриваю возможность использования этой концепции в нашем производственном коде, это плохая идея? Что может пойти не так?

Ответы [ 3 ]

2 голосов
/ 13 сентября 2011

Я не вижу причин, по которым это не будет работать безопасно.Тем не менее, у меня есть подозрение, что на самом деле это будет медленнее, чем использование простого старого lock.Причина в том, что ReaderWriterLockSlim имеет в 2 раза больше служебных данных по сравнению с lock. 1 . Таким образом, вам понадобится выполнение кода в критической секции, чтобы потреблять достаточное количество циклов ЦП.чтобы преодолеть эту дополнительную нагрузку, чтобы достичь точки безубыточности.Если все, что вы делаете, это просто получаете доступ к Dictionary (или к какой-либо другой структуре данных _cacheEntries), то я сомневаюсь, что RWLS подходит для этой ситуации.Блокировки чтения-записи имеют тенденцию работать лучше в сценариях, где число читателей значительно превосходит число авторов и , когда защищенная часть кода длинная и вытянутая.Очевидно, вы должны сделать свои собственные тесты, потому что пробег будет значительно варьироваться в зависимости от многих других факторов.Степени параллелизма и количество ядер в аппаратном обеспечении могут дать вам большую пропускную способность с использованием RWLS, даже если простой точечный анализ безубыточности изначально не одобрил их.


1 Основано на моих собственных тестах.ReaderWriterLock было примерно в 5 раз больше.

2 голосов
/ 13 сентября 2011

Да, есть технические причины, по которым это не очень хорошая идея.Если Действие выдает исключение при обновлении данных, то данные теперь находятся в неизвестном и очень вероятно поврежденном состоянии.Ваш код безоговорочно снимает блокировку записи, что означает, что другие потоки теперь получают доступ к частично обновленному общему состоянию.Это рецепт катастрофы.

2 голосов
/ 13 сентября 2011

Мне кажется, что это нормально, за исключением того, что код выглядит очень громоздким

Я бы, вероятно, реализовал IDisposable как:

public class WriteLock : IDisposable
{
   ReaderWriterLockSlim _rwlock;
   public WriteLock(ReaderWriterLockSlim rwlock ) 
   { 
      _rwlock = rwlock;
      _rwlock.EnterWriteLock(); 
   }
   public void Dispose()
   {
      _rwlock.ExitWriteLock(); 
   }
}

Использование:

 private ReaderWriterLockSlim _rwlock = new ReaderWriterLockSlim();

 //...

 using (new WriteLock(_rwlock)) //<-- here the constructor calls EnterWriteLock
 {
      _cacheEntries.Remove(name);

 } //<---here Dispose method gets called automatically which calls ExitWriteLock

Аналогично, вы можете реализовать класс UpgradeableReadLock, реализующий интерфейс IDisposable.

Идея состоит в том, что вы можете создать экземпляр одноразового класса в конструкции using, которая гарантирует, что в конструкторе вы входите в блокировку записи, вызывая метод EnterWriteLock(), а когда он выходит из области видимости, метод Dispose() вызывается автоматически (CLR), вызывая метод ExitWriteLock().

Обратите внимание, что он не будет располагать ReaderWriterLockSlim объектом; он будет располагать WriteLock объектом, который является просто оболочкой. ReaderWriterLockSlim будет таким же в пользовательском классе.

...