Альтернативные причины для индекса были вне границ массива в словаре .Net

Я понимаю, что одной из главных причин индекса за пределами ошибки границ объекта Dictionary является столкновение потоков. (Чтение и запись в один и тот же словарь одновременно). Однако я столкнулся с недоумением, когда столкновение потоков не является достаточным объяснением.

Здесь ситуация: Я написал код, который реализует словарь небезопасным способом для многопоточной обработки.

Код реализован как веб-служба на двух серверах, сервере A и сервере B. Доступ к разлочкам осуществляется через балансировщик нагрузки, который будет отправлять запросы серверам A и B циклическим способом.

Теперь вот сложная часть. Ошибка ТОЛЬКО появляется на сервере A и никогда на сервере B. Согласно нашей аппаратной команде, оба сервера идентичны. Хотя столкновение потоков по сути является случайным процессом, оно должно по-прежнему влиять на оба моих сервера одинаково. Я вижу 50+ экземпляров ошибки на одном сервере и 0 на другой. Статистически маловероятно, что конфликты потоков происходят только на одном из моих серверов, в то время как другой работает без ошибок.

Я уже изменяю приложение, чтобы сделать его потоком более безопасным, но какие другие причины могут существовать для этой ошибки, возникающей во время операции Вставки объекта Dictionary?

Ответ 1

Хотя столкновение потоков по сути является случайным процессом

Совсем нет. Это критически зависит от времени. И время может быть повторяемо, системы, как правило, согласуются с конкретными шаблонами. Диагностический инструмент для расчёта нитей, такой как Microsoft Research CHESS, работает путем ввода случайных задержек в выполнение потока. Чтобы система выпала из такого шаблона. Как это иногда делает сам по себе, но только раз в неделю или около того. Это случайное, просто не случайное, чтобы когда-либо дать вам шанс отладить проблему.

Таким образом, если один сервер терпит неудачу, а другой ничего не значит. Вероятно, это связано с балансировкой нагрузки. Вы просто не сможете точно определить причину, потому что вы не можете узнать, что произошло в 50 раз. Этого недостаточно.

Ответ 2

Это, вероятно, надуманно, но знаете ли вы, знаете ли, что ваши соединения с двумя серверами через балансировщик нагрузки равны? (Я действительно ничего не знаю о том, как работает балансировка нагрузки, так что это может быть глупой мыслью от get-go.)

Я просто думаю, скажу, что у вас немного больше латентности сети в вашем соединении с сервером B, чем с сервером A. Это может обеспечить достаточное расстояние между клиентскими запросами на этом сервере, что приведет к доступу к словарю, позволяя вам уйти с вашим многопоточным код, который не является безопасным.

Если запросы доходят до сервера А немного быстрее, это может сделать разницу, которая дает вам ошибки вне диапазона.

Как я уже сказал, вероятно, надуманный - просто идея. Я подумал, что это не помешает выбросить его там.

Ответ 3

Я не могу объяснить, почему он не работает на одном сервере, но не на другом. Однако ваши проблемы являются многопоточными.

Как вы могли заметить, это не будет работать в многопоточной среде:

if (!dict.ContainsKey("myKey"))
    dict.Add("myKey", value);

То же самое для:

if (dict.ContainsKey("myKey"))
    return dict["myKey"];

Что может вас удивить, так это то, что TryGetValue также не является потокобезопасным:

MyObject obj;
return dict.TryGetValue("myKey", out obj) ? obj : null;

Ссылка: http://www.grumpydev.com/2010/02/25/thread-safe-dictionarytkeytvalue/