2010-02-08 1 views
6

Этот вопрос относится к очень конкретному и распространенному сценарию, в котором словарь используется для кеширования по требованию в многопоточной среде. Чтобы избежать блокировки потока, рекомендуется протестировать существующий элемент кэша вне блокировки синхронизации, но если впоследствии нам нужно добавить элемент, который считается записью в словарь, и поэтому большинство рекомендаций, которые я читал в stackoverflow, это то, что вам необходимо заблокировать чтение, а также запись, потому что внутреннее состояние словаря может быть изменено вызовами add().Является ли словарь <K,V> надежным потоком для одновременного чтения и добавления?

Однако, просматривая Microsoft AjaxControlToolkit (класс scriptObjectBuilder), код фактически выполняет TryGet() вне любых блокировок и блокирует только Add() новые элементы в словаре. Я могу видеть, как это возможно, если ведро, в которое элемент помещается, никогда не изменяется после добавления, но мое подозрение в том, что это неправильно и может быть источником ошибок.

Спасибо.

ОБНОВЛЕНИЕ Выполнение документации .Net Я считаю, что описанный шаблон действительно ошибочен. Однако мне было интересно, поддерживает ли это конкретная реализация словаря и что AjaxControlToolkit полагался на это (что было бы сомнительно). Изучая код в Reflector, я уверен, что это действительно неправильно, метод Dictionary.Resize() перераспределяет количество ведер и перемещает элементы ведра вокруг, поэтому любой поток в середине TryGet() может потенциально работать по нестабильным данным.

ОБНОВЛЕНИЕ Дефект был уже зарегистрирован против AjaxControlToolkit на codeplex. См:

+0

Вы пробовали? клиентские сценарии обычно однопоточные. применяются стандартные оговорки. –

+0

Вы пробовали написать тестовый жгут для этого самостоятельно? Мне было бы интересно узнать, не вызывает ли это проблемы. –

+0

Это код ajax на стороне сервера.Есть синхронизация потоков, потому что она использует статический словарь в местах для кэширования данных. – redcalx

ответ

-1

Чтобы ответить на мой вопрос, следуя некоторым инвестициям: Изучив код для словаря, я вижу, что метод Dictionary.Resize() перераспределяет количество внутренних ведер, используемых для хранения данных, и перераспределяет содержимое ведра, элементы находятся в правильном ведре на основе их хэш-кода. Таким образом, любой поток в середине TryGet() рискует работать с нестабильными данными.

В качестве альтернативы возможный подход с низким уровнем блокировки для класса словаря может заключаться в том, чтобы блокировать блокировку только методом Resize().

+0

Ниже приведен ответ Rex M ниже, и я столкнулся с этим в производственном коде. Синхронизируйте всю вещь, если только * каждый * поток не доступен только для чтения. ReaderWriterLock [Slim] подходит. – FMM

5

Tess Ferrandez имеет excellent blog post о многопоточности проблемы с родовыми словарях:

метод FindEntry ходит по словарю, пытаясь найти ключ , Если несколько потоков делают это в одно и то же время, особенно если словарь изменен тем временем, вы можете оказаться в бесконечном цикле в FindEntry, что приведет к высокому поведению процессора, и процесс может зависать.

1

Я бы сказал, что это определенно ошибка в AjaxControlToolkit и порекомендует вам поднять ошибку на CodePlex. Он должен использовать ReaderWriteLock (Slim).

+0

Спасибо. По-видимому, он уже был зарегистрирован в 2007 году, но я добавил некоторые данные. См. Ссылки в сообщении (вверху) – redcalx