PLINQ AsParallel()。ForAll()访问资源

时间:2012-11-19 01:04:46

标签: c# multithreading linq plinq

假设我在X,Y空间中有多个Particle s,我想将它们全部归一化,使得平均X和Y为0.

串行实施:

public void Normalise()
{
  double avgX = 0.0;
  double avgY = 0.0;

  foreach (Particle p in Particles)
  {
    avgX += p.X;
    avgY += p.Y;
  }

  avgX /= (double)Particles.Count;
  avgY /= (double)Particles.Count;

  foreach (Particle p in Particles)
  {
    p.X -= avgX;
    p.Y -= avgY;
  }
}

这很有效,而且性能也不错,因为它是O(n),但它“令人尴尬地平行”。看看我的PLINQ实现:

public void PNormalise()
{
  double avgX = 0.0;
  double avgY = 0.0;

  Particles.AsParallel().ForAll(p =>
  {
    avgX += p.X;
    avgY += p.Y;
  });

  avgX /= (double)Particles.Count;
  avgY /= (double)Particles.Count;

  Particles.AsParallel().ForAll(p =>
  {
    p.X -= avgX;
    p.Y -= avgY;
  });
}

我不确定这里的表现,但我认为它会更好。问题是,粒子都是随机跳动的。我只能假设+=avgX上的avgY操作相互竞争,即使它们已经相当原子化了。

我能做些什么来解决它吗?我不能lock他们因为他们不是对象,但我不确定我是不是想要,因为锁定不是很贵?

3 个答案:

答案 0 :(得分:5)

您可以通过Parallel LINQ的常规机制来绕过对锁(或原子操作)的需求:

var avgX = Particles.AsParallel().Average(p => p.X);
var avgY = Particles.AsParallel().Average(p => p.Y);

Particles.AsParallel().ForAll(p => { p.X -= avgX; p.Y -= avgY });

由于数字的总和是一个O(N)操作,如果这部分占用了很长一段时间,我会感到非常惊讶。

答案 1 :(得分:1)

使用

Particles.AsParallel().ForAll(p =>
{
    Interlocked.Add(avgX, p.X);
    Interlocked.Add(avgY, p.Y);
}

做一个线程安全的原子加法。有关详细信息,请参阅Interlocked Class

的文档

答案 2 :(得分:0)

实际上,并行化这个O(n) - 算法不会产生更好的性能,因为你有与计算指令大致相同的内存访问量。