每个线程都会获得自己可以更改的副本吗?他们都共享同一个吗?它是线程安全吗?
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的情况下,任务并行库是管理并发访问还是我必须这样做?
答案 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的情况下,任务并行库是管理并发访问还是我必须这样做?
您必须自己管理并发访问。