LINQ中的C#并行性

LINQ中的C#并行性,c#,parallel-processing,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 .AsPar

我想知道这是否是在下面的代码中计算
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));
}
publicstaticIEnumerable解析(objectobj,refintx)
{
x+=10;
返回新列表();
}
公共静态作废查询(列表obj)
{
int x=0;
var结果=obj
.天冬酰胺()
.Select(o=>Parse(o,ref x))
.骨料((a,b)=>a.Concat(b));
}

这是我的代码的缩短版本。我希望
x
成为所有并行执行Parse的
静态计数器。我希望这不会令人困惑

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

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

public static IEnumerable<Object> Parse(Object obj, ref int x)
{
    Interlocked.Add(ref x, 10);
    return new List<Object>();
}
publicstaticIEnumerable解析(objectobj,refintx)
{
联锁。添加(参考x,10);
返回新列表();
}

绝对不安全。 您需要使用
联锁

public static IEnumerable<Object> Parse(Object obj, ref int x)
{
    Interlocked.Add(ref x, 10);
    return new List<Object>();
}
publicstaticIEnumerable解析(objectobj,refintx)
{
联锁。添加(参考x,10);
返回新列表();
}

我建议使用另一种方法来解决这个问题,正如前面所建议的,在并行代码中引入同步结构会影响其工作,但是,如果您仍然需要它,那么您的原始代码需要类似于联锁/锁定的东西来确保线程安全

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

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));
}
公共类MyClass
{
公共int x;
公共目标o;
}
公共静态IEnumerable解析(对象obj)
{
MyClass c=新的MyClass();
c、 x+=10;
c、 o=
//将c添加到列表的实例
返回新列表();
}
公共静态作废查询(列表obj)
{          
var结果=obj
.天冬酰胺()
.选择(o=>Parse(o))
//结果的类型为IEnumerable
var sum=result.sum(a=>a.x);
var aggregate=结果聚合((a,b)=>a.o.Concat(b.o));
}

这是一个无锁/同步的解决方案,没有性能影响,也没有竞争条件。始终对于线程,尝试将线程的内容设置为本地,然后对每个线程变量应用sum之类的函数。

您确定真的需要它吗?你需要它做什么?正确同步对值的访问意味着性能大大降低,因为操作不能再完全并行工作。此外,不应使用
Aggregate/Concat
将序列转换为平坦序列。它会导致嵌套N个级别的查询;很快就会坏的。只需使用
SelectMany
,它就是专门为此设计的。我真的需要它,因为最后我需要检查所有的解析方法是否都计算了精确数量的(非常复杂的)对象。非常感谢您提供有关
SelectMany
的说明!听起来你需要做点什么。听起来不一定需要在线程之间共享整数变量。例如,除了对象序列之外,每个方法还可以返回一个整数,表示它所表示的任何计数,从而允许将两个结果值聚合在一起。这将允许每个操作完全并行地完成其工作。对优化的伟大建议!这很聪明!但是你怎么看待创建元组然后聚合它们呢?任何你喜欢的DS,尽管需要检查其适用性,但要保持其线程本地性,除了并发类型没有DS在默认情况下是线程安全的,并且会导致竞争条件或损坏