在parallel.foreach循环中捕获变量

时间:2015-03-04 18:04:44

标签: c# task-parallel-library

每个线程都会获得自己可以更改的副本吗?他们都共享同一个吗?它是线程安全吗?

X var;
Parallel.Foreach(ls , it => Op(var, It));

Op对var做了些什么。我假设如果var是一个引用(比如一个List<string>),那么就会有一个所有线程共享的工作和我的工作来实现安全更新。如果X是int,该怎么办?

如果var是List<string>但是为空,该怎么办?如果一个线程创建一个新的List并将其写入var?其他线程是否会看到var的新值?

我已经做了一些测试,但我想确保我所看到的是真实的,而不是我写错了测试。

最后一种情况(null)似乎每个线程最终都有自己的List<string>

编辑: 好像我需要区分

   X var;
    Parallel.Foreach(ls , it => Op(var, It));

   X var;
    Parallel.Foreach(ls , it => {
          ....
          var = <something>
          ....
      });

即lambda本身修改变量。在这种情况下,它共享。但在函数(Op)案例中,关于传递值的通常规则适用

在共享var的情况下,任务并行库是管理并发访问还是我必须这样做?

3 个答案:

答案 0 :(得分:0)

仅复制变量引用。不是可变数据。就好像所有局部变量都通过方法调用传递给匿名委托。

MSDN (Anonymous Methods (C# Programming Guide))

  

对外部变量n的引用被称为在被捕获时被捕获   委托已创建。与局部变量不同,a的生命周期   捕获变量延伸到引用的代理   匿名方法有资格进行垃圾回收。

答案 1 :(得分:0)

该lambda的所有调用都将访问同一个变量。每次调用都不会有单独的副本。

闭包将由编译器转换为与以下内容相当的士气:

public class ClosureClass
{
    public X var;
    public void method1(Y it)
    {
        Op(var, it);
    }
}

IEnumerable<Y> ls = null;
ClosureClass closure = new ClosureClass();
closure.var = null;
Parallel.ForEach(ls, closure.method1);

将创建一个类来表示闭包,一个实例将在方法的开头创建,lambda的主体将映射到闭包类中的方法,以及所有用法关闭变量将是该类中的字段。正如您在此处所看到的,closure.method1的所有调用都将最终访问同一实例的相同字段,这是一个单数变量。

答案 2 :(得分:0)

您按var的{​​{1}}按值传递,Op无法更改Op,因为var只有副本。

修改

  

即lambda本身修改变量。在这种情况下,它共享。但在函数(Op)案例中,关于传递值的通常规则适用

是的,如果你想共享变量,那么你应该通过lambda本身引用它,或者通过引用(Op)在Op中传递它,假设Op(ref var,it)的签名也发生了变化) ,而不是价值。

  

在共享var的情况下,任务并行库是管理并发访问还是我必须这样做?

您必须自己管理并发访问。