Потокобезопасность при записи в два словаря .net одним и тем же методом - PullRequest
2 голосов
/ 24 августа 2011

Меня попросили сделать словари в потоке классов безопасными.

Мое первое предложение состояло в том, чтобы реализовать поточный словарь, сообщество разработчиков .net уже работало над этим, но оно было отклонено.

Код выглядит примерно так:

class Example()
{
    Dictionary<string, string> dic1;
    Dictionary<string, string> dic2;
     public void Example()
     {
        dic1 = new Dictionary<string,string>(10);
        dic2 = new Dictionary<string,string>(10);
     }

     public string Method1(string param1)
     {
            if(dic1.ContainsKey(param1))
            {
                return dic1[param1];
            }

            if(IsValidParam(param1))
            {
                dic1.Add(param1, param1);
                return param1;
            }

            try
            {   
                var params = GetValidParams(param1);
                if(params.Count > 0)
                {
                    foreach(var param in params)
                    {
                        if(!isValirParam(param)
                            continue;

                        dic1.Add(param1, param);

                        if(!dic2.ContainsKey(param1))
                        {
                            dic2.Add(param, param1);
                        }

                        return param;
                    }
                }
                else
                {
                    dic2.Add(param1, param1);
                    return param1;
                }
            }
            catch(Exception ex)
            {
                .....
            }

            return param1;
     }
}

Это только один из многих методов, которые имеют доступ на чтение и запись к обоим словарям внутри одного и того же метода.

Я думал о рефакторинге и использовании «ReaderWriterLockSlim» при каждом добавлении и возврате, но я не знаю, собирается ли это сделать этот поток безопасным.

У вас есть идеи, как к этому подойти?Я открыт для предложений ...

Заранее спасибо за ваше время

Ответы [ 3 ]

3 голосов
/ 24 августа 2011

Самый простой способ в этом случае убедиться, что вы получите то, что ожидаете, - это использовать lock.

class Example()
{
    Dictionary<string, string> dic1;
    Dictionary<string, string> dic2;
    private Object syncRoot;
     public void Example()
     {
        dic1 = new Dictionary<string,string>(10);
        dic2 = new Dictionary<string,string>(10);
        syncRoot = new Object();
     }

     public string Method1(string param1)
     {
         lock(syncRoot) {
            if(dic1.ContainsKey(param1))
            {
                return dic1[param1];
            }

            if(IsValidParam(param1))
            {
                dic1.Add(param1, param1);
                return param1;
            }

            try
            {   
                var params = GetValidParams(param1);
                if(params.Count > 0)
                {
                    foreach(var param in params)
                    {
                        if(!isValirParam(param)
                            continue;

                        dic1.Add(param1, param);

                        if(!dic2.ContainsKey(param1))
                        {
                            dic2.Add(param, param1);
                        }

                        return param;
                    }
                }
                else
                {
                    dic2.Add(param1, param1);
                    return param1;
                }
            }
            catch(Exception ex)
            {
                .....
            }

            return param1;
          }
     }
}

Обратите внимание, что это замедлит работу (у блокировки есть некоторые накладные расходы, и, в частности, у вас не будет двух потоков, выполняющих что-либо внутри блока блокировки одновременно), но это гарантирует, что Thread2, выполняющий этот метод, не сможет изменить что-то среднее между тем, когда Thread1 проверил значение и когда он попытается использовать результат этого теста, чтобы что-то сделать. Также не требуется .net 4, так что вы сможете его использовать.

edit - Стоит также упомянуть, что если у вас есть какие-либо другие методы, модифицирующие любой словарь, вы захотите заблокировать их таким же образом. Ключевым моментом здесь является то, что только один поток может возиться с вещами в любой момент времени.

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

Как и Тридус сказал, что вам, скорее всего, придется обернуть все содержимое Method1 в lock.Тем не менее, это может быть один сценарий, в котором ReaderWriterLockSlim может действительно помочь.Вы можете заблокировать чтение при первом поиске в dic1.Если это удастся, вы можете спастись, даже не взяв эксклюзивную блокировку записи.Если поиск не удался, вы переходите на блокировку записи.Конечно, вам придется тестировать, но если ожидается, что первоначальный поиск в большинстве случаев будет успешным, вы можете получить много параллелизма.

0 голосов
/ 24 августа 2011

В .NET 4 уже есть ConcurrentDictionary<T,V>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...