Я собирался опубликовать вопрос, но разобрался с ним заранее и решил опубликовать вопрос и ответ - или, по крайней мере, мои наблюдения.
При использовании анонимного делегата в качестве WaitCallback, когда ThreadPool.QueueUserWorkItem вызывается в цикле foreach, создается впечатление, что одно и то же значение foreach передается в каждый поток.
List< Thing > things = MyDb.GetTheThings();
foreach( Thing t in Things)
{
localLogger.DebugFormat( "About to queue thing [{0}].", t.Id );
ThreadPool.QueueUserWorkItem(
delegate()
{
try
{
WorkWithOneThing( t );
}
finally
{
Cleanup();
localLogger.DebugFormat("Thing [{0}] has been queued and run by the delegate.", t.Id );
}
});
}
Для коллекции из 16 экземпляров Thing в Things я заметил, что каждое «Thing», переданное WorkWithOneThing, соответствует последнему элементу в списке «вещей».
Я подозреваю, что это потому, что делегат обращается к внешней переменной 't'. Обратите внимание, что я также экспериментировал с передачей Thing в качестве параметра анонимному делегату, но поведение оставалось неправильным.
Когда я повторно проанализировал код для использования именованного метода WaitCallback и передал Thing 't' методу, вуаля ... i-й экземпляр Things был правильно передан в WorkWithOneThing.
Урок параллелизма, наверное. Я также представляю, что семейство Parallel.For решает эту проблему, но эта библиотека не была для нас вариантом в данный момент.
Надеюсь, это спасет кого-то еще.
Говард Хоффман