c# – 当有封闭时我是否需要使用互锁?

考虑这段代码在计数器周围创建一个闭包:

uint counter = 0xFFFF;
someList.AsParallel().ForEach(a => { uint tmp = ++counter });

(请暂时搁置在并行foreach中使用计数器的明显问题).

tmp会评估为0x0000还是0x1FFFF?

我的理由:要将计数器从0xFFFF递增到0x10000,至少需要一个可被多线程中断的双字节CPU指令.如果它被中断,则有可能只更新一个字节的计数器 – 它可以暂时设置为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));
    }
}

And if I get rid of AsParallel, am I completely safe?

只要在迭代时列表或计数器没有变异,你应该没问题.从您的示例中,您无法知道您正在使用的数据的实际位置,但假设一切都是方法范围本地,您会没事的.

点赞