我有一个小的测试应用程序同时执行两个线程.一个增加静态长_值,另一个递减它.我已经通过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不是处理器的原子,它看起来像:
a = b不是处理器的原子,它看起来像:
>从内存中将数据加载到寄存器中
>增量数据
>存储数据
没有锁可能是:
>从内存将数据加载到寄存器X中
>从内存将数据加载到寄存器Y中
>增量数据(以X为单位)
>递减数据(以Y为单位)
>存储数据(从X)
>将数据存储回(从Y)//在这种情况下,增量丢失.
但这并不是缓存一致性,而是一个更高级的功能.
所以,锁定不能确保缓存是同步的.缓存同步是一个不依赖于代码的处理器内部功能.你可以阅读它here.
当一个内核将值写入内存,然后当第二个核心尝试读取该值时,它将不会在其缓存中具有实际副本,除非其缓存条目无效,从而发生缓存未命中.而这个高速缓存未命中将缓存条目更新为实际值.