Безопасен ли поток словаря <K, V> для одновременного чтения и дополнений? - PullRequest
6 голосов
/ 08 февраля 2010

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

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

Спасибо.

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

UPDATE Дефект был уже зарегистрирован в AjaxControlToolkit в кодекплексе. См:

Ответы [ 3 ]

5 голосов
/ 08 февраля 2010

У Тесс Феррандез отличное сообщение в блоге о проблемах с многопоточными словарями:

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

1 голос
/ 08 февраля 2010

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

0 голосов
/ 11 февраля 2010

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

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

...