6

私はParallel.ForEach一般的にC#、、、および.NETを初めて使用します。何千もの場所を含む検索を並列化したい。場所ごとに、大圏距離を計算します。それは私がさまざまなコアに広げたい計算です。私の質問は、このMSDN TPLの例のように、スレッドローカル変数が1つしかない場合、どうすればよいですか?結果として、私は、、、、、、およびそのオプションを確認しましたが、同等性を追加、インクリメント、デクリメント、またはテストしているだけではありません。並行して実行されている複数のスレッドで、全体的に最も短いオブジェクトを返したいInterlockedAddCompareExchangeDecrementExchangeIncrementRead距離。私の腸は、これは簡単で、距離をラップする小さなオブジェクトを作成できるはずだと言っていますがLocation、各スレッドから最良の答えを取得して、それらの中から最短距離を選択するにはどうすればよいですか?非並列バージョンは次のとおりです。

Location findClosestLocation(Location myLocation, List<Location> allLocations)
{
  double closest = double.MaxValue;
  Location closestLoc = null;
  foreach (Location aLoc in allLocations)
  {
    if (aLoc != myLocation)
    {
      double d = greatCircle(myLocation, aLoc);
      if (d < closest)
      {
        closest = d;
        closestLoc = aLoc;
      }
    }
  }
  return closestLoc;
}

良いアドバイスを提供しているように見えるDDJブログ投稿を見ましたが、それが最良のアドバイスであるかどうか疑問に思いました。著者が配列をループしているのを見て、これを行うためのより機能的な方法はないのだろうかと思います。機能の世界では、、、を使用しmapます。lambdamin

4

1 に答える 1

11

ここでの最も簡単なオプションは、PLINQに切り替えることです。

Location findClosestLocation(Location myLocation, List<Location> allLocations)
{
     return allLocations
               .AsParallel()
               .Min(location => greatCircle(myLocation, location));
}

そうは言っても、これは基本的に並列構造を使用した単なる集約です。Parallelクラスに固執したい場合は、いくつかのオプションがあります。1つのオプションは、ロックを使用して、ブロック内でこれを自分で同期することです。全体的なパフォーマンスが低下するため、これはお勧めしません。

より良いオプションは、ローカル状態を提供するParallel.ForEachメソッドを使用することです。彼らはあなたがこれを次のように書き直すことを可能にするでしょう:

Location findClosestLocation(Location myLocation, List<Location> allLocations)
{
  double closest = double.MaxValue;
  Location closestLoc = null;
  object sync = new object();

  Parallel.ForEach<Location, Tuple<double,Location>(
      allLocations,
      () => new Tuple(double.MaxValue, null),
      (location, loopState, localState) =>
      {
          double d = greatCircle(myLocation, aLoc);
          if (d < localState.Item1)
              return new Tuple(d, aLoc);
          else
              return localState;
      },
      localState =>
      {
          lock(sync)
          {
              if (localState.Item1 < closest)
              {
                  closest = localState.Item1;
                  closestLoc = localState.Item2;
              }
          }
      }
  );
  return closestLoc;
}

集計にローカル状態を使用する方法については、ブログで詳しく説明しています。これにより、基本的に、処理要素ごとに1つのロックではなく、スレッドごとに1つのロック操作に操作が変更されるため、単純なロックソリューションよりもはるかに高いスループットが得られます。

于 2010-07-23T22:19:54.943 に答える