C# · 12月 27, 2021

c# – 锁定语句如何确保内部处理器同步?

我有一个小的测试应用程序同时执行两个线程.一个增加静态长_值,另一个递减它.我已经通过ProcessThread.ProcessorAffinity确保线程与不同的物理(无HT)内核相关联,以强制执行内部处理器通信,并确保它们在执行时间内重叠大量时间.

当然,以下不会导致零:

for (long i = 0; i < 10000000; i++){ _value += offset;}

所以,逻辑的结论是:

for (long i = 0; i < 10000000; i++){ Interlocked.Add(ref _value,offset);}

当然导致零.

但是,以下也导致零:

for (long i = 0; i < 10000000; i++){ lock (_syncRoot) { _value += offset; }}

当然,lock语句确保读取和写入不会被重新排序,因为它采用了一个完整的栅栏.但是,我找不到有关处理器缓存同步的信息.如果没有任何缓存同步,我会认为在两个线程完成之后我应该看到0的偏差?

有人可以向我解释锁/ Monitor.Enter / Exit如何确保处理器缓存(L1 / L2缓存)同步?

解决方法 在这种情况下,缓存一致性不取决于锁定.如果使用lock语句,则可以确保您的汇编器命令不会混合.
a = b不是处理器的原子,它看起来像:

>从内存中将数据加载到寄存器中
>增量数据
>存储数据

没有锁可能是:

>从内存将数据加载到寄存器X中
>从内存将数据加载到寄存器Y中
>增量数据(以X为单位)
>递减数据(以Y为单位)
>存储数据(从X)
>将数据存储回(从Y)//在这种情况下,增量丢失.

但这并不是缓存一致性,而是一个更高级的功能.

所以,锁定不能确保缓存是同步的.缓存同步是一个不依赖于代码的处理器内部功能.你可以阅读它here.

当一个内核将值写入内存,然后当第二个核心尝试读取该值时,它将不会在其缓存中具有实际副本,除非其缓存条目无效,从而发生缓存未命中.而这个高速缓存未命中将缓存条目更新为实际值.