平行的.ForEach -对修改过的闭包进行访问

本文关键字:闭包 访问 ForEach 修改 | 更新日期: 2023-09-27 18:06:38

我已经阅读了许多关于访问已修改闭包的其他问题,因此我了解基本原理。但是,我不知道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例程,并且开始/结束时间变量直到下一次循环时才会更新。这是正确的还是我应该使用局部变量来确保没有问题?

平行的.ForEach -对修改过的闭包进行访问

正在访问修改过的闭包,因此它确实适用。但是,当你使用它的时候,你没有改变它的值,所以假设你没有改变UpdateUsageStats里面的值,你在这里没有问题。

Parallel.Foreach等待执行结束,然后才更改startTimeendTime的值。

"访问修改过的闭包"仅在捕获范围离开发生捕获的循环并在其他地方使用时才会导致问题。例如

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

在您的示例中,执行捕获的lambda不会离开循环(每次都在循环中完全执行Parallel.ForEach调用)。

您仍然会得到警告,因为编译器不知道Parallel.ForEach是否导致将lambda存储以供以后调用。因为我们比编译器知道的更多,所以可以安全地忽略警告。