【问题标题】:Parallel.ForEach - Access To Modified Closure Applies?Parallel.ForEach - 访问修改后的闭包是否适用?
【发布时间】:2014-06-04 18:30:31
【问题描述】:

我已经阅读了许多关于 Access to Modified 闭包的其他问题,因此我了解了基本原理。不过,我还是不知道——Parallel.ForEach 有同样的问题吗?

以下面我重新计算上周用户使用统计的sn-p为例:

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 例程,并且开始/结束时间变量在下一次循环之前不会更新。这是正确的还是我应该使用局部变量来确保没有问题?

【问题讨论】:

    标签: c# .net closures parallel.foreach


    【解决方案1】:

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

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

    【讨论】:

    • 那么是否有曾经出现Parallel.Foreach 会出现关闭问题或者仅仅是因为我使用了 lambda 而发出警告的情况?
    • @drew_w 确实有。如果你在 Prallel.Foreach 中改变它的值
    • 但唯一的方法是将ref 变量传递给调用例程或任务。对吗?
    • @drew_w 不,您可以在匿名方法中执行此操作,例如 user => { Console.Writeline(endTime); endTime = DateTime.Now;}
    【解决方案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
    

    在您的情况下,执行捕获的 lamda 不会离开循环(每次都在循环内完全执行 Parallel.ForEach 调用)。

    您仍然会收到警告,因为编译器不知道 Parallel.ForEach 是否导致 lambda 被存储以供以后调用。由于我们比编译器了解更多,因此我们可以放心地忽略该警告。

    【讨论】:

      猜你喜欢
      • 2011-09-29
      • 2011-07-28
      • 2010-09-23
      • 2015-11-08
      • 2012-09-14
      • 2011-02-05
      • 2011-04-29
      • 2010-12-13
      相关资源
      最近更新 更多