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

时间:2011-01-23 07:31:54

标签: c# .net 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);
});

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

 ......... 1
 ......... 2
 ......... 3
 ......... 4
 ......... 5
 ......... 
 ......... 
 ......... n

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

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

3 个答案:

答案 0 :(得分:14)

Interlocked.Increment将返回递增的值。

所以,

int counter = 0;

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

    var counterNow = Interloced.Increment( ref counter );

    Debug.WriteLine(" ......... counter: " + counterNow);
});

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

编辑,很久以后:

作为解释:

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

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

答案 1 :(得分:7)

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

答案 2 :(得分:1)

我不确定,但我认为这可能是由于变量捕获在lambdas中的工作方式。您是否尝试将其置于单独的函数中,或将变量移到外部并将其声明为静态?