Словарь C # с ReaderWriterLockSlim - PullRequest
2 голосов
/ 25 августа 2009

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

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

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

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

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

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


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

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();

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

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

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

Ответы [ 3 ]

6 голосов
/ 26 августа 2009

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

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.) Если вам нужна общая блокировка для всех ваших классов-оболочек, то она должна быть статической.

1 голос
/ 07 марта 2013

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

0 голосов
/ 25 августа 2009

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

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

ReadWriterLockSlim lck = new ReadWriterLockSlim();

...

lck.EnterReadLock();
try
{
    ...
}
finally
{
    lck.ExitReadLock();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...