Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/docker/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# PLINQ AsParallel().ForAll()访问资源_C#_Multithreading_Linq_Plinq - Fatal编程技术网

C# PLINQ AsParallel().ForAll()访问资源

C# PLINQ AsParallel().ForAll()访问资源,c#,multithreading,linq,plinq,C#,Multithreading,Linq,Plinq,假设我在X,Y空间中有许多粒子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)Partic

假设我在X,Y空间中有许多
粒子
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;
  }
}
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;
  });
}
这是可行的,性能也不错,因为它是O(n),但它“令人尴尬地平行”。看看我的PLINQ实现:

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;
  }
}
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
上的
+=
操作相互竞争,即使它们已经相当原子化了

我能帮你修一下吗?我不能
锁定它们,因为它们不是对象,但我不确定我是否愿意,因为锁定不是很昂贵吗?

使用

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

执行线程安全的原子加法。有关更多信息,请参阅的文档。

您可以通过并行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)运算,如果这部分占用了大量时间,我会非常惊讶。

实际上,并行化这个O(N)算法不会带来更好的性能,由于您的内存访问量与计算指令大致相同。

更好的解决方案可能是执行并行求和,自动为您处理此问题。
互锁。只添加重载
(ref int,int)
(ref long,long)
,但是我使用的是double,我不知道JIT编译器是否会这样做,但是它很可能会通过修改来更改指令。关于CLR和扩展(如SSEYes)的更多信息,但该算法仍然是内存受限的,因此CPU仍然大部分空闲,但它所做的少量工作现在均匀地分布在其内核中。@MathiasBecher:O(N)这样的问题实际上非常适合并行性。如果您的问题规模越来越大,您可以在大多数情况下向其投入更多硬件,以达到与以前类似的水平。我同意,除非你有N=10^7这样的东西,否则它可能有点微不足道。没有“相当原子化”这样的东西,一个操作要么是原子的,要么不是。