Parallel.For(0, ACount, i =>
{
Console.WriteLine($"Initial {sfm.DataCache.Count}");
return ;
});
Initial 412
Initial 3691
Initial 6778
Initial 6908
Initial 6666
Initial 7332
Initial 7464
Initial 7598
Initial 356
Initial 7599
Initial 7611
Initial 7611
Initial 7611
Initial 2255
Initial 7671
Initial 7671
Initial 6796
而加上
var aa = sfm.DataCache.Count;
Parallel.For(0, ACount, i =>
{
Console.WriteLine($"Initial {sfm.DataCache.Count}");
return ;
});
结果就是正确的
Initial 10000
Initial 10000
Initial 10000
Initial 10000
Initial 10000
Initial 10000
Initial 10000
Initial 10000
Initial 10000
Initial 10000
Google的回答是:
DataCache.Count 时,这个值可能只停留在行情线程所在核心的缓存或寄存器中,而执行 Parallel.For 的那十几个线程所在的核心读取的是自己缓存里的旧值(或者是该值在内存中由于被频繁修改而处于“脏”状态的中间值)。Parallel.For 内部启动任务时,虽然会有一定的同步开销,但在循环体内部读取 Count 属性时,如果没有显式的锁或内存屏障,CPU 为了优化性能可能不会强制从主内存重新加载该值。这就导致你看到的 Initial 结果在 356 到 7611 之间乱跳,因为各核心看到的“进度”不同。var aa = ... 就变正确了?Parallel.For 启动之前,你在主线程显式访问了 Count 属性。这迫使主线程去主内存(或 L3 缓存)获取最新的 10000。Parallel.For 启动的那一刻,主线程已经同步了最新的数据状态,并且后续分发给各个子线程的任务(闭包)会携带这个已经“稳定”下来的状态。Volatile.Read (推荐) Volatile.Read 强制每次都从内存读取:
但是并没有给出有效的解决方案,反而是
var aa = sfm.DataCache.Count;
更有效,算了不找BUG了