[英]Do I need to use interlocked when there is a closure?
考虑这段代码在counter
周围创建一个闭包:
uint counter = 0xFFFF;
someList.AsParallel().ForEach(a => { uint tmp = ++counter });
(请暂时搁置在并行foreach中使用计数器的明显问题)。
tmp
会评估为0x0000还是0x1FFFF?
我的理由:要将counter
从0xFFFF递增到0x10000,至少需要一个可被多线程中断的双字节CPU指令。 如果它被中断,则有可能只更新一个字节的counter
- 它可以暂时设置为0x00000或0x1FFFF。
我应该把它写成:
uint counter = 0xFFFF;
someList.AsParallel().ForEach(a => { uint tmp = Interlocked.Increment(counter) });
...?
如果我摆脱AsParallel
,我完全安全吗?
是的,你需要Interlocked.Increment
,闭包不会改变这个操作不是线程安全的事实。 什么闭包将把你的lambda表达式提升到一个显示类中,并在每次迭代时重复使用相同的类,这将导致多个线程递增计数器。
反编译看起来像这样:
public class C
{
[CompilerGenerated]
private sealed class <>c__DisplayClass0_0
{
public uint counter;
internal void <M>b__0(int a)
{
uint num = this.counter + 1u;
this.counter = num;
}
}
public void M()
{
C.<>c__DisplayClass0_0 <>c__DisplayClass0_ = new C.<>c__DisplayClass0_0();
<>c__DisplayClass0_.counter = 65535u;
List<int> source = new List<int> {
1,
2,
3
};
source.AsParallel<int>().ForAll(new Action<int>(<>c__DisplayClass0_.<M>b__0));
}
}
如果我摆脱
AsParallel
,我完全安全吗?
只要在迭代时列表或计数器没有变异,你应该没问题。 从您的示例中,您无法知道您正在使用的数据的实际位置,但假设一切都是方法范围本地,您会没事的。
对。 Parallel是多线程的语法糖。 你仍然需要线程安全。 如果你是单线程,你显然不需要Interlocked(或线程安全)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.