2014-06-04 3 views
4

Я прочитал ряд других вопросов о доступе к модифицированному закрытию, поэтому я понимаю основной принцип. Тем не менее, я не мог сказать - у Parallel.ForEach есть те же проблемы?Parallel.ForEach - Доступ к модифицированному закрытию применяется?

Рассмотрим следующий фрагмент кода, где я пересчитывать статистику использования для пользователей за последнюю неделю в качестве примера:

var startTime = DateTime.Now; 
var endTime = DateTime.Now.AddHours(6); 
for (var i = 0; i < 7; i++) 
{ 
    // this next line gives me "Access To Modified Closure" 
    Parallel.ForEach(allUsers, user => UpdateUsageStats(user, startTime, endTime)); 

    // move back a day and continue the process 
    startTime = startTime.AddDays(-1); 
    endTime = endTime.AddDays(-1); 
} 

Из того, что я знаю этого кода foreach должен запустить свой UpdateUsageStats процедуру сразу и начать/конечные временные переменные не будут обновляться до следующего цикла цикла. Это правильно или я должен использовать локальные переменные, чтобы убедиться, что проблем нет?

ответ

4

Вы : Доступ к модифицированному закрытию, поэтому он применяется. Но, вы не меняете его значение во время его использования, поэтому, если вы не меняете значения внутри UpdateUsageStats, у вас нет проблем.

Parallel.Foreach ожидает завершения выполнения, и только после этого вы меняете значения в startTime и endTime.

+0

Так ли * когда-либо * ситуация, когда 'Parallel.Foreach' будет иметь проблему с закрытием или это предупреждение только потому, что я использую лямбда? –

+0

@drew_w Действительно. Если бы вы изменили свое значение в то время как внутри Prallel.Foreach – i3arnon

+0

Но единственный способ сделать это - с переменной 'ref', переданной в вызывающую подпрограмму или задачу. Верный? –

2

«Доступ к модифицированному закрытию» приводит только к проблемам, если область захвата выходит из цикла, в котором происходит захват, и используется в другом месте. Например,

var list = new List<Action>(); 
for (var i = 0; i < 7; i++) 
{ 
    list.Add(() => Console.WriteLine(i)); 
} 
list.ForEach(a => a()); // prints "7" 7 times, because `i` was captured inside the loop 

В вашем случае лямд делать захват не оставляет петлю (Parallel.ForEach вызова выполняется полностью в цикле, каждый раз вокруг).

Вы по-прежнему получаете предупреждение, потому что компилятор не знает, будет ли или нет Parallel.ForEach, чтобы лямбда хранилась для последующего вызова. Поскольку мы знаем больше, чем компилятор, мы можем смело игнорировать предупреждение.

Смежные вопросы