Ах, доброе старое закрытие по переменной итератора; p
Перемещение int iter
:
int iter = i;
ThreadPool.QueueUserWorkItem((o) => {
makeServiceCall(iter);
});
В настоящее время вы захватываете что-то, что меняется с циклом, и обычно после цикла к моменту запуска потоков. В качестве альтернативы используйте arg o
:
ThreadPool.QueueUserWorkItem((o) => {
int iter = (int)o;
makeServiceCall(iter);
}, i);
Это передает коробочную копию i
в момент, когда элемент находится в очереди, поэтому он не изменится.
В случае, если проблема не ясна: переменные, которые записаны в лямбду, сохраняют свою исходную точку объявления - это полное лексическое замыкание. i
внутри QueueUserWorkItem
- это не «значение i
во время создания этой лямбды», а скорее «значение i
сейчас ». В большинстве случаев цикл for
значительно ускоряет создание / захват потока, поэтому к тому моменту, когда потоки запустились, цикл for
завершился, и значение i
вышло за пределы для массива.