Строго говоря — нет.
В Hashtable нет ни volatile read/write, ни memory barriers, т.е. вполне возможно зачитка по уже имеющемуся ключу еще не вставленного значения или же наоборот: зачитка ключа от уже удалённого значения.
Т.е. ситуация, когда мы не можем найти элемент, который "только что добавлен из другого потока", или нашли и прочитали элемент, который "уже удалён другим потоком" — это вполне нормальный сценарий для конкурентного доступа. Но когда мы по валидному ключу читаем невалидное значение, то это уже натуральный ууупппсс. ))
M>2) Я пробовал прогонять код для более короткого цикла (напр. 100000) и clr и jvm имеют одну производительность. Такое ощущение что CLR GC не справляется. Это также подтверждается вызовом GCSettings.LatencyMode на LowLatency. Цифры сразу взлетают вверх.
Это боксинг. Твоя псевдопотокобезопасность Hashtable зиждется на том, что ключи и значения в таблице представлены типом-ссылкой, чтение и запись которых атомарны на поддерживаемых CRL платформах. В общем же случае как в Dictionary<> в дотнете или аналогичном решении на Java, где ключ или значение не влезают в машинное слово — никаких потокобезопасных решений без специальных для этого техник (типа read/write lock, даже пусть их ligh-варианта на спин-локах) нет и быть не может. Всё, что нельзя записать/прочитать через Interlocked.Exchange (или его аналог) — всё идет мимо кассы.
Кароч, сама постановка задачи изначально ошибочна — нельзя требовать того, что быть не может.