【问题标题】:increment a count value outside parallel.foreach scope在 parallel.foreach 范围之外增加一个计数值
【发布时间】:2020-07-19 05:35:38
【问题描述】:

如何在 parallel.foreach 循环范围之外增加整数值?在并行循环之外同步访问对象的最简单方法是什么?

var count = 0;
Parallel.ForEach(collection, item =>
{
  action(item);
  // increment count??
}

【问题讨论】:

    标签: c# synchronization locking parallel-processing


    【解决方案1】:

    我喜欢打死马! :)

    从多个线程增加计数的“最简单”的方法是:

    Interlocked.Increment(ref count);
    

    但正如其他人指出的那样:如果您在 Parallel.ForEach 内部执行此操作,那么您可能做错了什么。

    我怀疑由于某种原因您正在使用ForEach,但您需要一个正在处理的项目的索引(它永远不会与Parallel.ForEach 一起使用)。我接近了吗?为什么需要计数?你想施展什么样的巫毒魔法?

    更新:

    如果您的密钥是 URI 并且值是 TAnswer,那么您使用 ConcurrentDictionary 似乎是安全的。只要您不尝试使用计数来引用集合中的元素,我认为没有问题。

    终于...

    如果您需要计数器,请使用 Parallel.For 循环...它会为您安全地递增计数器。

    【讨论】:

    • 在给定起始 uri 列表的情况下,使用多个线程获取数千个网页。我很想知道到目前为止已经处理了多少。使用并发字典似乎实际上满足了我的需求:ConcurrentDictionary<uri tanswer></uri>。然后我可以使用字典的 Count 属性。在某些情况下,处理一系列项目不会产生任何结果,例如获取的 html 文档。在这种简单的情况下,我认为我可以只使用一个具有适当同步的变量,并且想知道如何做到这一点。
    • 带有计数变量的Parallel.ForEach 的另一种用途:计算图像之间的逐像素相似度。
    • 当我尝试这个时,我得到以下错误,知道吗?属性或索引器可能不会作为 out 或 ref 参数传递。我有一个 int 我绑定为标签的内容,以跟踪已处理的项目数。我很想增加它的线程安全,但到目前为止还没有乐趣。
    【解决方案2】:

    以这种方式使用Interlocked.Increment方法。

    int count = 0;
    Parallel.ForEach(users, (u) =>
    {
        var currentCount = Interlocked.Increment(ref count);
        Log(String.Format("Step {0} of {1}", currentCount, users.Count));
    });
    

    【讨论】:

    • 谢谢,我只想显示进度百分比。
    【解决方案3】:

    使用Interlocked.Increment

    我不会在一个并行的foreach 中增加的东西(当然,除非您使用Interlocked.Increment 或其他一些锁定机制)。那不是它的用途。 Parallel foreach 应该只在共享状态下不会导致副作用的操作运行。在并行 foreach 中增加一个值将导致您极有可能试图避免的问题。

    【讨论】:

    • 同意。我的问题是他为什么需要这个计数。检查所有动作是否完成?应该有更好的同步方式。
    • 如何跟踪相当长时间运行(分钟)的事情的进度?我需要这样做以向客户反馈,以便他们衡量等待时间并查看进度。
    • 我也需要报告完成百分比。因此,如果我有 10,000 次迭代,我想跟踪完成百分比并为每增加 1% 发送一条消息。因此,当第 100 次迭代完成时,我需要检测到这一点。
    【解决方案4】:

    Parallel.Foreach 附带一个额外的overload,当您只想计算循环内的内容时,它非常适合这种情况。

        int totalCount = 0;
        Parallel.ForEach(files, () => 0, (file, loopState, localCount) =>
        {
            if(ProcessFile(file))
               localCount++;
        
            return localCount;
        
        }, localCount => Interlocked.Add(ref totalCount, localCount));
    
        Console.WriteLine($"Processed {totalCount}/{files.Length} files.");
    

    您可以在循环体中注入一个线程本地计数变量并像往常一样增加/减少它。这样,您就不会在每个循环周期中都有 InterLocked 调用。相反,在每个并行任务结束时只有一个 Interlocked.Add 调用汇总 totalCount。

    请注意,使用此方法时,您不应在循环体内使用totalCount。如果您的问题需要从循环体内访问 totalCount,您必须使用 InterLocked.Incerment 方法,如其他答案中所述。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-24
      相关资源
      最近更新 更多