在 parallel.foreach 循环中捕获变量
captured variables in a parallel.foreach loop
是否每个线程都有自己的可以更改的副本?他们都共享同一个吗?它是线程安全的吗?
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 的情况下,任务并行库管理并发访问还是我必须这样做?
仅复制变量引用。不是可变数据。就好像所有的局部变量都通过方法调用传递给匿名委托。
每MSDN (Anonymous Methods (C# Programming Guide))
A reference to the outer variable n is said to be captured when the
delegate is created. Unlike local variables, the lifetime of a
captured variable extends until the delegates that reference the
anonymous methods are eligible for garbage collection.
该 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);
将创建一个 class 来表示闭包,一个实例将在方法开始时创建,lambda 的主体将映射到闭包 class 中的一个方法,并且所有封闭变量的使用将是 class 中的字段。正如您在此处看到的,closure.method1
的所有调用最终将访问同一实例的同一字段,这是一个单一变量。
你在Op
中按值传递var
,所以Op
不能改变var
,因为Op
只有它的副本。
编辑:
ie the lambda itself modifies the variable. In this case its shared. But in the function (Op) case the usual rules about pass by value apply
是的,如果你想共享变量,那么你应该通过 lambda 本身引用它或者通过引用将它传递给 Op
(Op(ref var,it)
,假设 Op
的签名也已更改),而不是按值。
In the case where var is shared does the task parallel library manage concurrent access or do I have to do it?
您必须自己管理并发访问。
是否每个线程都有自己的可以更改的副本?他们都共享同一个吗?它是线程安全的吗?
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 的情况下,任务并行库管理并发访问还是我必须这样做?
仅复制变量引用。不是可变数据。就好像所有的局部变量都通过方法调用传递给匿名委托。
每MSDN (Anonymous Methods (C# Programming Guide))
A reference to the outer variable n is said to be captured when the delegate is created. Unlike local variables, the lifetime of a captured variable extends until the delegates that reference the anonymous methods are eligible for garbage collection.
该 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);
将创建一个 class 来表示闭包,一个实例将在方法开始时创建,lambda 的主体将映射到闭包 class 中的一个方法,并且所有封闭变量的使用将是 class 中的字段。正如您在此处看到的,closure.method1
的所有调用最终将访问同一实例的同一字段,这是一个单一变量。
你在Op
中按值传递var
,所以Op
不能改变var
,因为Op
只有它的副本。
编辑:
ie the lambda itself modifies the variable. In this case its shared. But in the function (Op) case the usual rules about pass by value apply
是的,如果你想共享变量,那么你应该通过 lambda 本身引用它或者通过引用将它传递给 Op
(Op(ref var,it)
,假设 Op
的签名也已更改),而不是按值。
In the case where var is shared does the task parallel library manage concurrent access or do I have to do it?
您必须自己管理并发访问。