Interlocked.Increment()没有按照我在任务并行库中所期望的方式工作

Bon*_*ver 6 .net c# parallel-processing

我有一个Parallel.ForEach()循环,它接受一个URL列表并下载每个URL以进行一些额外的处理.在我的循环之外,我已经声明了一个循环计数器变量,并且在循环体内我使用Interlocked.Increment()认为这将是保持"线程安全"方式的最佳方法,即在执行每个循环交互时增加计数.

int counter = 0;

Parallel.ForEach(urlList, (url, state) =>
{
    // various code statments

    Interlocked.Increment( ref counter );

    Debug.WriteLine(" ......... counter: " + counter);
});
Run Code Online (Sandbox Code Playgroud)

我原以为我会看到类似的东西:

 ......... 1
 ......... 2
 ......... 3
 ......... 4
 ......... 5
 ......... 
 ......... 
 ......... n
Run Code Online (Sandbox Code Playgroud)

但我得到的是16"......... 0"(这是因为我有一个双核四核计算机,有8个本机核心,但是启用了超线程,总共有16个核心).然后我将开始看到计数器在大多数情况下正常递增,但有时我会在Debug输出中看到重复或甚至三重计数器值.

使用Parallel.ForEach()计算循环迭代的最佳方法是什么?谢谢你的建议.

小智 14

Interlocked.Increment将返回递增的值.

所以,

int counter = 0;

Parallel.ForEach(urlList, (url, state) =>
{
    // various code statments

    var counterNow = Interloced.Increment( ref counter );

    Debug.WriteLine(" ......... counter: " + counterNow);
});
Run Code Online (Sandbox Code Playgroud)

应该在递增时返回计数器值.

编辑,很久以后:

作为解释:

当您在多处理器/多核计算机上运行并具有多个应用程序线程时,您需要注意,您看到的变量值可能无法反映该变量的实际当前状态.这是因为每个CPU或芯片或套接字可能有许多单独的缓存,并且您的线程可以读取缓存的值(保存命中以读取主内存)

如果您只想读取由多个线程更新的值,则应使用Interlocked.Read()以保证您具有当前值counter.


Ita*_*aro 7

这是因为Increment + WriteLine不是原子的.
可能是thread1递增counter,然后thread2再次递增它,然后两个线程以相同的值到达WriteLine部分counter.