Как стать автором
Обновить
54
-3

Пользователь

Отправить сообщение
Теперь убедили, потенциально тут может возникнуть ArgumentOutOfRangeException.
Да, каждый конкретный случай лучше рассматривать отдельно и выявлять наболее оптимальное решение, также стоит производить замеры на различных CLR, поскольку результаты иногда сильно плавают.
Моя цель — поделиться идеями, подвергуть их критике и приблизиться истине. Вот с примененим лока на делегате уже нашли изъян, и я признаю, что оказался не прав. :)

Думаю, это заставляет работать умы людей и более глубоко разбираться в вопросах.
Да, признаю, в этом предположении я оказался не прав. Нужно задавать более надёжный контекст для lock'а. Пример подправлю.
Изначально я придерживаюсь такого определения:
Словарь, не являясь потокобезопасным классом, способен работать в условиях нескольких потоков, но может давать ненадёжные результаты.
Пример интересный, но я не вижу аналогии с текущим случаем. Словарь внутри себя не дожидается выполнения потоков и не делает предположений, о значениях переменных. Грубо говоря, это просто массив, который может увеличиваться, сохраняя индексы элементов, с ненадёжным параллельным чтением (один поток может упустить изменения, только что внесённые другим).
Можно два определения дать потокобезопасности:
1. Гаранития того, что коллекция вообще будет работать в условиях нескольких потоков
2. Гарантия того, что при записи/удалении/замене элемента одним потоком, второй изменения сразу же увидит

Сейчас я придерживаюсь второго, более сильного. Словарь, не являясь потокобезопасным классом, способен работать в условиях нескольких потоков, но может давать ненадёжные результаты.
Запись элементов идёт только под lock'ом. То есть я допускаю лишь ситуацию с ненадёжным параллельным чтением, которая обрабатывается под тем же lock'ом.
Не вижу причин для падения даже при параллельном Clear.
Возможно, для других сценариев это критично, что накладывает ограничения на применение, но для конкретного допустимо.
Пока убедительных аргументов, почему метод используется неправильно, я не услышал. Исключений нет, как выяснили, другой элемент тоже не придёт в результате. Если даже элемен вдруг потеряется, что крайне маловероятно, то в нашем случае ничего серьёзного не произойдёт, создадим новый вместо прежнего.
Её, как будто, гарантируют CLR и компилятор при инициализации статической переменной
    [CompilerGenerated]
    private sealed class <>c
    {
        public static readonly <>c <>9 = new <>c();

(декомпиляция)

Сама идеология работы метода при правильной имплементации должна гарантировать отсутствие всяких исключений. Если исключения есть, значит, плохо реализован метод, в нём баг.
Нет, я не проверял все возможные сценарии и отталкиваюсь лишь от того, что TryGetValue возвращает true и сам элемент, если он присутствует в словаре по ключу, либо false если его там нет или он только что асинхронно добавлен в процессе чтения другим потоком (и на этот случай выполняется повторное чтение в критической секции).
Чтобы воспроизвести такое нужны примеры намного похитрее, чем наш. :)
По сути TryGetValue гарантирует, что не будет исключений из какого бы мы потока не работали со словарём. Может только false вернуться при несинхронном добавлении элемента из другого потока (поскольку словарь непотокобезопасный). Этот второй случай обрабатывается повторным чтением в lock.

Мне так видится реализация.
Насчёт пересоздания тоже ещё вопрос, но в нашем случае даже при таком неудачном раскладе исключения точно не будет, просто создастся новый экземпляр и по ключу заменит старый в словаре.
Пока не отработал конструктор объект никуда не может быть добавлен, поскольку на него ещё нигде нет внешних ссылок.
Выглядит так, что в худшем случае мы можем лишь потерять элемент, уже находящийся в словаре, что приведёт к его пересозданию извне, но если элемент по ключу найден, то чтение безопасно, поскольку индеск в массиве за ним закрепляется навсегда.
Поясните…

Ссылка на экземпляр объекта становится в первую очередь доступной в конструкторе, а потом уже вовне (если мы её не передали куда-то до завершения выполнения конструктора из самого конструктора). Исключение составляет случай создания объекта без вызова конструктора FormatterServices.GetUninitializedObject. Это насколько мне известно.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность