关闭时是否需要使用互锁?
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
,我就完全安全了吗?
是的。 Parallel 只是 multi-threading 的语法糖。你还需要thread-safe。如果你是单线程的,你显然不需要互锁(或线程安全)。
是的,您需要 Interlocked.Increment
,关闭不会改变此操作不是线程安全的事实。闭包将做的是将您的 lambda 表达式提升到显示 class 和 re-use 中,每次迭代都相同 class,这将导致多个线程递增计数器。
反编译看起来像这样:
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));
}
}
And if I get rid of AsParallel
, am I completely safe?
只要在迭代时列表或计数器没有发生变化,就应该没问题。从你的例子中不可能知道你正在使用的数据的实际位置,但假设一切都是本地方法范围,你会没事的。
考虑这段围绕 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
,我就完全安全了吗?
是的。 Parallel 只是 multi-threading 的语法糖。你还需要thread-safe。如果你是单线程的,你显然不需要互锁(或线程安全)。
是的,您需要 Interlocked.Increment
,关闭不会改变此操作不是线程安全的事实。闭包将做的是将您的 lambda 表达式提升到显示 class 和 re-use 中,每次迭代都相同 class,这将导致多个线程递增计数器。
反编译看起来像这样:
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));
}
}
And if I get rid of
AsParallel
, am I completely safe?
只要在迭代时列表或计数器没有发生变化,就应该没问题。从你的例子中不可能知道你正在使用的数据的实际位置,但假设一切都是本地方法范围,你会没事的。