LINQ中的C#并行性

时间:2015-05-14 17:22:46

标签: c# parallel-processing

我想知道这是否是在下面的代码中计算x的安全方式。

public static IEnumerable<Object> Parse(Object obj, ref int x)
{
    x += 10;
    return new List<Object>();
}

public static void Query(List<Object> obj)
{
    int x = 0;

    var result = obj
        .AsParallel()
        .Select(o => Parse(o, ref x))
        .Aggregate((a, b) => a.Concat(b));
}

这是我的代码的缩短版本。我希望x成为某种static counter for all parallel executions of Parse。我希望这不会令人困惑。

3 个答案:

答案 0 :(得分:4)

您的代码有竞争条件。即使变量x通过引用传递,它在所有并发执行中仍然是相同的变量,因此向它添加十个需要是原子的。

解决此问题的一种方法是使用Interlocked.Add方法而不是+=

public static IEnumerable<Object> Parse(Object obj, ref int x)
{
    Interlocked.Add(ref x, 10);
    return new List<Object>();
}

答案 1 :(得分:4)

绝对不安全。 您需要使用Interlocked类。

public static IEnumerable<Object> Parse(Object obj, ref int x)
{
    Interlocked.Add(ref x, 10);
    return new List<Object>();
}

答案 2 :(得分:2)

我建议采用不同的方法来解决这个问题,如前所述,在并行代码中引入同步构造会影响其工作,如果你仍然需要它,那么你的原始代码需要像Interlocked / lock之类的东西才能使它成为线程安全的但是

更好的方法是每个线程都有一个本地计数器并在最后聚合,如下所示:

public class MyClass
{
  public int x;
  public object o;
}

public static IEnumerable<MyClass> Parse(Object obj)
{
    MyClass c = new MyClass();
    c.x += 10;
    c.o  = <some new object>
    // Add c to instance of List<MyClass>
    return new List<MyClass>();
}

public static void Query(List<Object> obj)
{          
    var result = obj
        .AsParallel()
        .Select(o => Parse(o))

   // result is of type IEnumerable<MyClass>

   var sum = result.Sum(a=>a.x);

   var aggregate = result.Aggregate((a, b) => a.o.Concat(b.o));
}

这是一个无锁定/同步解决方案,没有性能损失,也没有竞争条件。始终用于线程,尝试为线程创建局部内容,然后为每个单独的线程变量应用sum等函数。