2009-10-20 5 views
1

Я хотел бы использовать шаблон if-lock-if для проверки наличия объекта в словаре в многопоточной среде. Таким образом, код я рассматриваю как выглядит так:Доступ к .NET-словарю в многопоточной среде

private IDictionary<string, SomeType> m_dic = new Dictionary<string, SomeType>(); 

private SomeType GetSomeObject(string key) 
{ 
    SomeType obj; 
    if (!m_dic.TryGetValue(key, out obj)) 
    { 
    lock(m_dic) 
    { 
     if (!m_dic.TryGetValue(key, out obj)) 
     { 
     m_dic[key] = obj = CreateSomeObject(key); 
     } 
    } 
    } 
    return obj; 
} 

Я действую исходя из предположения, что даже если другой поток вставки объекта в том же ключе прямо сейчас, TryGetValue не будет возвращать частично набор ссылок (например, вещь не существует в .NET, не так ли?), скорее она вернет null, и поэтому мы войдем в защищенный раздел и повторим проверку.

Мой вопрос является моим предположением правильно, и код является правильным?

Спасибо.

EDIT

Позвольте мне бросить в ограничение. Словарь фактически является словарем одиночных объектов. Итак, как только запись занята, она никогда не изменяется. Точно так же, как свойство экземпляра синглтона - после его установки оно никогда не изменяется. Учитывая это ограничение, можно ли использовать шаблон if-lock-if?

+0

Редактирование: Нет, внутренние буферы Словаря по-прежнему не являются потокобезопасными. –

ответ

5

Edited для ясности:

Нет это очень плохо идея. Вы можете играть в игру if-lock на чем-то простом и атомарном (int), но словарь - это класс с несколькими движущимися частями. Чтение и запись должны быть синхронизированы в любое время, см. Раздел ThreadSafety на странице this MSDN page.

+2

Я знаю это.Но я хочу знать, будет ли его внутреннее состояние повреждено, если один поток задает (TryGetValue), а другой поток пишет. Или возвращенный ответ просто будет нулевым, если на самом деле есть действительный объект, уже вставленный другим потоком. Если состояние повреждено - код неправильный. Если, однако, TryGetValue просто вернул null, тогда код будет прав, потому что он повторяет вызов внутри защищенного раздела. Пожалуйста, уточните свой ответ. – mark

+0

Я отредактировал свой вопрос, чтобы немного почистить вещи. В статье MSDN говорится об общих случаях, мое дело особенное. – mark

+0

А что, если заказ отличается? Предположим, что сначала добавлен ключ, а TryGetValue возвращает значение null, потому что еще нет значения добавления значения, которое приводит нас к защищенной секции с блокировкой. – mark

0

В .NET невозможно получить частично заданную ссылку. Все операции чтения/записи являются атомарными.

Код выглядит хорошо :) Но не забудьте добавить синхронизацию к другим операциям, таким как INSERT и UPDATE. не

3

От http://www.yoda.arachsys.com/csharp/singleton.html:

Блокировка на объекты, которые другие классы могут получить доступ и запирают на (например, типа) риски, проблемы с производительностью и даже тупики. Это мой основной стиль - по возможности, только блокировка объектов, специально созданных с целью блокировки, или какой документ они должны быть заблокированы для определенных целей (например, для ожидания/пульсирования очереди). Обычно такие объекты должны быть закрытыми для класса, в котором они используются. Это помогает значительно упростить процесс написания потоков.

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

private static readonly object m_padlock = new object(); 
private IDictionary<string, SomeType> m_dic = new Dictionary<string, SomeType>(); 

private SomeType GetSomeObject(string key) 
{ 
    SomeType obj; 

    lock (m_padlock) 
    { 
    if (!m_dic.TryGetValue(key, out obj)) 
    { 
     m_dic[key] = obj = CreateSomeObject(key); 
    } 
    } 

    return obj; 
} 
+0

Но m_dic является конфиденциальным, поэтому аргумент для m_padlock слаб. Я согласен со второй частью. –

+0

Наличие выделенного объекта блокировки не является проблемой. Мой вопрос концентрируется на применении if-lock-if применимости к словарям. – mark

+0

Я отредактировал мой вопрос, чтобы немного разобраться. – mark

0

Я думаю, что ReaderWriterLock лучше, чем подход в этом сенарио. Кроме того, у вас есть несколько читателей, но только один писатель.

http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlock.aspx

+0

Точка - это попытка избежать штрафа за блокировку, когда объект уже присутствует в словаре. – mark

+0

'ReaderWriterLockSlim' заменил' ReaderWriterLock'. –

+0

readerwriterlock не так уж и хорош: http://msdn.microsoft.com/en-us/magazine/cc163599.aspx –

2

См my comments to the correct reply. Важная часть: если вы замените словарь в Hashtable в вашем примере, этот подход будет работать.

Смежные вопросы