【问题标题】:local variable inside parallel loops thread safety并行循环内的局部变量线程安全
【发布时间】:2013-03-13 22:15:38
【问题描述】:

我在嵌套的parallel.foreach 中有一个变量 (cnt)。我运行这个程序,显然它运行良好。有人知道这段代码真的是线程安全的吗?是否可以在parallel 循环中定义变量?

谢谢。

    object obj = new object();
    int total=0;

    Parallel.For(0, 2, i =>
    {
        Parallel.For(0, 1000000, j =>
        {
            int cnt = 0;
            if ((arr[i, j] % 2) == 0)
            {
                Interlocked.Increment(ref cnt);
            }
            lock (obj)
            {
                total= total+ (cnt / 2);
            }
        });
    });

【问题讨论】:

    标签: c# parallel-processing parallel.foreach


    【解决方案1】:

    此代码是线程安全的 - 唯一的共享数据 (total) 已正确同步。即使不使用Interlocked.Increment(即:在您编辑之前),它也是线程安全的。

    然而,它并不高效

    使用local initializer and finally delegates 的重载会好得多。

    int total=0;
    Parallel.For(0, 2, 
    {
        Parallel.For(0, 1000000, 
          local => 0,
          (j, state, local) =>
          {
            if ((arr[i, j] % 2) == 0)
            {
               ++local;
            }
            return local;
          },
          local => Interlocked.Increment(ref total, local)
        );
    });
    

    【讨论】:

    • 很抱歉,请告诉我如何改变它以提高效率
    • 不知道您能否提供更多信息。不幸的是,我收到此错误方法'System.Threading.Tasks.Parallel.For的类型参数(int,int,System.Func,System.Func, System.Action)' 不能从用法中推断出来。尝试明确指定类型参数。
    • @mohammad 您需要确保本地类型显而易见 - 或明确指定类型。我在这里写过博客:reedcopsey.com/2010/01/22/…
    • 感谢您提供信息丰富的博客。正如您所提到的,各个线程之间的局部变量在最终委托中合并。在我的程序中,我需要将局部变量除以 2。但是当我使用这段代码时,结果是错误的。锁定(obj){总计=(总计+(本地/2));我想知道是否让我知道如何解决这个问题。
    • @mohammad 但是,您的原始代码没有意义 - 因为您的“本地”只能是 0 或 1(您只能在单个循环迭代中使用它),所以 /2将始终为零,因为您使用的是 int 值...您可能需要重新考虑这里的逻辑。
    【解决方案2】:

    这段代码确实是安全的,因为lock 语句充当了一个完整的隔离操作。它确保增量操作是原子的,不会受到来自其他线程的竞争

    【讨论】:

    • @pst 锁是必要的,因为使用了total。它在同一个语句中被读取和写入。如果没有lock,线程可能会在读取之后但在写入之前被中断,因此最终会产生不完整的数据
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-07
    • 2015-07-07
    • 1970-01-01
    • 2016-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多