Потоки в C#: как обеспечить безопасность в потоке с помощью loop-index?

В следующем примере кажется, что индекс i цикла for модифицируется независимо каждым потоком, что приводит к странным значениям i (кратно, даже значения больше, чем System.Environment.ProcessorCount-1) в DoWork_Threaded()

Как многопоточные циклы правильно выполняются в C#?

// Prepare all threads
Thread[] threads = new Thread[System.Environment.ProcessorCount];

// Start all threads
for (int i = 0; i < System.Environment.ProcessorCount; i++)
{
   threads[i] = new Thread(() => DoWork_Threaded(i));
   threads[i].Start();
}

// Wait for completion of all threads
for (int i = 0; i < System.Environment.ProcessorCount; i++)
{
   threads[i].Join();
}

2 ответа

Решение

Прежде всего, вместо того чтобы использовать собственную службу планирования потоков, почему бы не использовать параллельную библиотеку задач? Он уже имеет логику, которая определяет количество процессоров для планирования потоков.

Чтобы ответить на ваш реальный вопрос, вы закрываете переменную цикла. Смотрите мою статью о том, почему это не так:

https://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/

Короче: i переменная и переменные меняются. Когда ваша лямбда выполняется, она выполняется с текущим значением i, а не значение, которое он имел при создании делегата.

Использование локальных переменных с анонимными методами и потоками может быть сложным. Вам необходимо скопировать значение, которое используется анонимным методом при его изменении:

for (int i = 0; i < System.Environment.ProcessorCount; i++)
{
     int a = i;
     threads[i] = new Thread(() => DoWork_Threaded(a));
     threads[i].Start();
}

Это делает вывод рациональным на моей машине.

Другие вопросы по тегам