Да, каждый конкретный случай лучше рассматривать отдельно и выявлять наболее оптимальное решение, также стоит производить замеры на различных CLR, поскольку результаты иногда сильно плавают.
Моя цель — поделиться идеями, подвергуть их критике и приблизиться истине. Вот с примененим лока на делегате уже нашли изъян, и я признаю, что оказался не прав. :)
Думаю, это заставляет работать умы людей и более глубоко разбираться в вопросах.
Пример интересный, но я не вижу аналогии с текущим случаем. Словарь внутри себя не дожидается выполнения потоков и не делает предположений, о значениях переменных. Грубо говоря, это просто массив, который может увеличиваться, сохраняя индексы элементов, с ненадёжным параллельным чтением (один поток может упустить изменения, только что внесённые другим).
Можно два определения дать потокобезопасности:
1. Гаранития того, что коллекция вообще будет работать в условиях нескольких потоков
2. Гарантия того, что при записи/удалении/замене элемента одним потоком, второй изменения сразу же увидит
Сейчас я придерживаюсь второго, более сильного. Словарь, не являясь потокобезопасным классом, способен работать в условиях нескольких потоков, но может давать ненадёжные результаты.
Запись элементов идёт только под lock'ом. То есть я допускаю лишь ситуацию с ненадёжным параллельным чтением, которая обрабатывается под тем же lock'ом.
Пока убедительных аргументов, почему метод используется неправильно, я не услышал. Исключений нет, как выяснили, другой элемент тоже не придёт в результате. Если даже элемен вдруг потеряется, что крайне маловероятно, то в нашем случае ничего серьёзного не произойдёт, создадим новый вместо прежнего.
Сама идеология работы метода при правильной имплементации должна гарантировать отсутствие всяких исключений. Если исключения есть, значит, плохо реализован метод, в нём баг.
Нет, я не проверял все возможные сценарии и отталкиваюсь лишь от того, что TryGetValue возвращает true и сам элемент, если он присутствует в словаре по ключу, либо false если его там нет или он только что асинхронно добавлен в процессе чтения другим потоком (и на этот случай выполняется повторное чтение в критической секции).
По сути TryGetValue гарантирует, что не будет исключений из какого бы мы потока не работали со словарём. Может только false вернуться при несинхронном добавлении элемента из другого потока (поскольку словарь непотокобезопасный). Этот второй случай обрабатывается повторным чтением в lock.
Насчёт пересоздания тоже ещё вопрос, но в нашем случае даже при таком неудачном раскладе исключения точно не будет, просто создастся новый экземпляр и по ключу заменит старый в словаре.
Выглядит так, что в худшем случае мы можем лишь потерять элемент, уже находящийся в словаре, что приведёт к его пересозданию извне, но если элемент по ключу найден, то чтение безопасно, поскольку индеск в массиве за ним закрепляется навсегда.
Ссылка на экземпляр объекта становится в первую очередь доступной в конструкторе, а потом уже вовне (если мы её не передали куда-то до завершения выполнения конструктора из самого конструктора). Исключение составляет случай создания объекта без вызова конструктора FormatterServices.GetUninitializedObject. Это насколько мне известно.
Думаю, это заставляет работать умы людей и более глубоко разбираться в вопросах.
1. Гаранития того, что коллекция вообще будет работать в условиях нескольких потоков
2. Гарантия того, что при записи/удалении/замене элемента одним потоком, второй изменения сразу же увидит
Сейчас я придерживаюсь второго, более сильного. Словарь, не являясь потокобезопасным классом, способен работать в условиях нескольких потоков, но может давать ненадёжные результаты.
(декомпиляция)
Мне так видится реализация.
Ссылка на экземпляр объекта становится в первую очередь доступной в конструкторе, а потом уже вовне (если мы её не передали куда-то до завершения выполнения конструктора из самого конструктора). Исключение составляет случай создания объекта без вызова конструктора FormatterServices.GetUninitializedObject. Это насколько мне известно.