Словарь C # с ReaderWriterLockSlim

2

Я очень новичок в многопоточности, и по какой-то причине этот класс дает мне больше проблем, чем нужно.

Я создаю словарь в кеше ASP.net - он будет часто запрашиваться для отдельных объектов, периодически перечисляться и записываться крайне редко. Отмечу, что данные словаря почти никогда не меняются, я планирую, чтобы он истекал ежедневно с обратным вызовом для перестройки из базы данных, когда он покидает кеш.

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

Если я использую Lock, я считаю, что могу либо заблокировать токен, либо фактический объект, который я защищаю. Я не вижу, как сделать что-то подобное с помощью LockWriter Lock. Правильно ли я полагаю, что несколько экземпляров моей оболочки не будут правильно блокироваться, поскольку ReaderWriterLocks находятся вне каждой области?

Какова наилучшая практика для написания обертки? Построение его как статичного почти кажется излишним, поскольку основной объект поддерживается кешем. Синглтон, похоже, нахмурился, и я беспокоюсь о вышеупомянутых проблемах для отдельных случаев.

Я видел несколько реализаций подобных оболочек, но я не смог ответить на эти вопросы. Я просто хочу убедиться, что у меня есть твердое понимание того, что я делаю, вместо того, чтобы разрезать и прокладывать себе путь. Большое вам спасибо за помощь!


** Edit: Надеюсь, это более ясное резюме того, что я пытаюсь выяснить - **

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

В качестве примера можно сказать, что у меня есть следующее -

MyWrapperClass 
{
    ReaderWriterLockSlim lck = new ReaderWriterLockSlim();
    Do stuff with this lock on the underlying cached dictionary object...
}

MyWrapperClass wrapA = new MyWrapperClass();
MyWrapperClass wrapB = new MyWrapperClass();

Правильно ли я думаю, что блокировка lockA и wrapB не будут взаимодействовать, и что если wrapA и wrapB будут пытаться выполнять операции, это будет небезопасно?

2. Если это так, то каков наилучший способ "поделиться" данными блокировки?

Это приложение Asp.net - будет несколько страниц, которым необходимо получить доступ к данным, поэтому я делаю это в первую очередь. Какова наилучшая практика для обеспечения того, чтобы различные обертки использовали один и тот же замок? Должна ли моя обертка быть статичной или синглетной, которую используют все потоки, если не самая элегантная альтернатива?

Теги:
dictionary
caching
thread-safety

3 ответа

6
Лучший ответ

У вас есть несколько объектов словаря в кэше, и вы хотите, чтобы каждый из них блокировался независимо. "Лучший" способ - просто использовать простой класс, который сделает это за вас.

public class ReadWriteDictionary<K,V>
{
    private readonly Dictionary<K,V> dict = new Dictionary<K,V>();
    private readonly ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();

    public V Get(K key)
    {
        return ReadLock(() => dict[key]);
    }

    public void Set(K key, V value)
    {
        WriteLock(() => dict.Add(key, value));
    }

    public IEnumerable<KeyValuePair<K, V>> GetPairs()
    {
        return ReadLock(() => dict.ToList());
    }

    private V2 ReadLock<V2>(Func<V2> func)
    {
        rwLock.EnterReadLock();
        try
        {
            return func();
        }
        finally
        {
            rwLock.ExitReadLock();
        }
    }

    private void WriteLock(Action action)
    {
        rwLock.EnterWriteLock();
        try
        {
            action();
        }
        finally
        {
            rwLock.ExitWriteLock();
        }
    }
}

Cache["somekey"] = new ReadWriteDictionary<string,int>();

Также есть более подробный пример на странице справки ReaderWriterLockSlim в MSDN. Нетрудно сделать это родовым.

edit Чтобы ответить на ваши новые вопросы -

1.) Вы правильно wrapA, и wrapB не будет взаимодействовать. У них обоих есть свой экземпляр ReaderWriterLockSlim.

2.) Если вам нужна общая блокировка среди всех ваших классов-оболочек, тогда она должна быть статичной.

  • 0
    Я просто перерабатывал вопрос, когда вы отвечали. Это фактически полностью обходит проблемы, о которых я беспокоился. Я думал, что я бы кешировал словарь внутри и оболочка была бы внешней, но ваше решение определенно кажется более элегантным. Спасибо!
  • 0
    Perfect- Большое спасибо! Кэширование всего объекта, а не только словаря, кажется намного лучшим подходом. Я действительно ценю разъяснения, я боялся двигаться вперед, не будучи уверенным в том, что я делал, и я не мог найти надежного примера, который подтвердил бы тот или иной путь. Извините, что изменил вопрос о вас, когда вы отправляли! Опять же, я ценю всю помощь, которая прояснила ситуацию!
Показать ещё 2 комментария
0

ConcurrentDictionary делает все, что угодно, а затем и некоторые. Часть System.Concurrent.Collections

  • 1
    Спасибо! К сожалению, этого не было, когда я изучал это раньше. Здорово иметь это сейчас.
0

Стандартный способ блокировки: object lck = new object(); ... lock(lck) { ... } в этом случае объект lck представляет блокировку.

ReadWriterLockSlim не сильно отличается, его только в этом случае класс ReadWriterLockSlim представляет фактическую блокировку, поэтому везде, где вы бы использовали lck, теперь вы используете ReadWriterLockSlim.

ReadWriterLockSlim lck = new ReadWriterLockSlim();

...

lck.EnterReadLock();
try
{
    ...
}
finally
{
    lck.ExitReadLock();
}
  • 0
    Вот так у меня теперь установлена блокировка, но если мой объект-обертка имеет несколько экземпляров, я обеспокоен тем, что объекты lck не будут находиться в одной области видимости и, следовательно, не будут взаимодействовать, как предполагалось. Конечно, я мог бы сделать lck статической переменной или сделать весь класс-оболочку статическим, но я хочу посмотреть, нужно ли это, и исправить это. Спасибо!
  • 1
    Там тоже нет хорошего плана, убедитесь, что у вас есть только один такой класс-обертка. Фактически, перестаньте думать о нем как о классе-обёртке и думайте о нем как о фактическом классе, так как он использует словарь, являющийся деталями реализации этого класса. В этот момент вы не должны раскрывать словарь за пределами указанного класса.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню