1

我需要遍历数组中的每个双精度来执行“拉普拉斯平滑”,“混合值”与相邻双精度。

我会将存储的值保存在临时克隆数组中,最后更新原始值。

伪代码:

double[] A = new double[1000];
// Filling A with values...

double[] B = A.Clone as double[];

for(int loops=0;loops<10;loops++){ // start of the loop

    for(int i=0;i<1000;i++){ // iterating through all doubles in the array
    // Parallel.For(0, 1000, (i) => {

       double v= A[i];
       B[i]-=v;
       B[i+1]+=v/2;
       B[i-1]+=v/2;
       // here i'm going out of array bounds, i know. Pseudo code, not relevant.

    }
    // });
}
A = B.Clone as double[];

使用for它可以正常工作。“平滑”数组中的值。

Parallel.For()遇到了一些访问同步问题:线程正在发生冲突,并且某些值实际上没有正确存储。线程多次访问和编辑同一索引处的数组。

(我没有在线性数组中测试过这个,我实际上正在研究一个多维数组[x,y,z] ..)

我该如何解决这个问题?

我正在考虑为每个线程创建一个单独的数组,然后再进行求和......但我需要知道线程索引并且我在网络上的任何地方都没有找到。(即使使用完全不同的解决方案,如果存在“线程索引”,我仍然感兴趣......)。

我会接受任何解决方案。

4

2 回答 2

1

您可能需要该方法的更高级重载之一Parallel.For

public static ParallelLoopResult For<TLocal>(int fromInclusive, int toExclusive,
    ParallelOptions parallelOptions, Func<TLocal> localInit,
    Func<int, ParallelLoopState, TLocal, TLocal> body,
    Action<TLocal> localFinally);

使用线程本地数据执行 for 循环,其中迭代可以并行运行,可以配置循环选项,并且可以监视和操作循环的状态。

对于它所期望的所有各种 lambda,这看起来非常令人生畏。这个想法是让每个线程处理本地数据,最后合并数据。以下是如何使用此方法来解决您的问题:

double[] A = new double[1000];
double[] B = (double[])A.Clone();
object locker = new object();
var parallelOptions = new ParallelOptions()
{
    MaxDegreeOfParallelism = Environment.ProcessorCount
};
Parallel.For(0, A.Length, parallelOptions,
    localInit: () => new double[A.Length], // create temp array per thread
    body: (i, state, temp) =>
    {
        double v = A[i];
        temp[i] -= v;
        temp[i + 1] += v / 2;
        temp[i - 1] += v / 2;
        return temp; // return a reference to the same temp array
    }, localFinally: (localB) =>
    {
        // Can be called in parallel with other threads, so we need to lock
        lock (locker)
        {
            for (int i = 0; i < localB.Length; i++)
            {
                B[i] += localB[i];
            }
        }
    });

我应该提一下,上述示例的工作负载过于细化,因此我不希望并行化能够大幅提高性能。希望您的实际工作量更大。例如,如果您有两个嵌套循环,则仅并行化外循环将非常有效,因为内循环将提供急需的块状。


替代解决方案:您可以直接更新 B 数组,而不是为每个线程创建辅助数组,并且仅在处理分区边界附近的危险区域中的索引时才使用锁:

Parallel.ForEach(Partitioner.Create(0, A.Length), parallelOptions, range =>
{
    bool lockTaken = false;
    try
    {
        for (int i = range.Item1; i < range.Item2; i++)
        {
            bool shouldLock = i < range.Item1 + 1 || i >= range.Item2 - 1;
            if (shouldLock) Monitor.Enter(locker, ref lockTaken);
            double v = A[i];
            B[i] -= v;
            B[i + 1] += v / 2;
            B[i - 1] += v / 2;
            if (shouldLock) { Monitor.Exit(locker); lockTaken = false; }
        }
    }
    finally
    {
        if (lockTaken) Monitor.Exit(locker);
    }
});
于 2020-04-13T00:17:25.853 回答
0

好的,看来模数几乎可以解决我所有的问题。这是工作代码的真正简化版本:(大脚本是 3d 且未完成...)

private void RunScript(bool Go, ref object Results)
  {
    if(Go){
      LaplacianSmooth(100);
      // Needed to restart "RunScript" over and over
      this.Component.ExpireSolution(true);
    }
    else{
      A = new double[count];
      A[100] = 10000;
      A[500] = 10000;
    }
    Results = A;
  }

  // <Custom additional code> 
  public static int T = Environment.ProcessorCount;
  public static int count = 1000;
  public double[] A = new double[count];
  public double[,] B = new double[count, T];

  public void LaplacianSmooth(int loops){
    for(int loop = 0;loop < loops;loop++){

      B = new double[count, T];

      // Copying values to first column of temp multidimensional-array
      Parallel.For(0, count, new ParallelOptions { MaxDegreeOfParallelism = T }, i => {
        B[i, 0] = A[i];
        });

      // Applying Laplacian smoothing
      Parallel.For(0, count, new ParallelOptions { MaxDegreeOfParallelism = T }, i => {
        int t = i % 16;
        // Wrapped next and previous element indexes
        int n = (i + 1) % count;
        int p = (i + count - 1) % count;
        double v = A[i] * 0.5;
        B[i, t] -= v;
        B[p, t] += v / 2;
        B[n, t] += v / 2;
        });

      // Copying values back to main array
      Parallel.For(0, count, new ParallelOptions { MaxDegreeOfParallelism = T }, i => {
        double val = 0;
        for(int t = 0;t < T;t++){
          val += B[i, t];
        }
        A[i] = val;
        });
    }
  }

蚱蜢上的多线程线性拉普拉斯平滑

线程没有“碰撞”,正如“质量加法”(总和)的结果所证实的那样,该结果恒定为 20000。

谢谢大家的提示!

于 2020-04-13T00:08:46.133 回答