C#で並列計算してみた

C#

プログラムの中で並列計算を行うということをやったことなかったので並列計算にチャレンジしてみました。

意外と簡単!

たとえばC#でForループを書くとき

for(int i = 0; i < 10; i++){
  Console.WriteLine("hogehoge");
}

みたいな感じで書くと思いますが基本的にはこれが

Pararell.For(0,10, i=>
{
  Console.WriteLine("hogehoge");
});

てな感じになるだけです。 (あ、System.Threading の名前空間を使います。) 超簡単やん。と思ってました。

ドツボにはまった点

ある1~N(Nは任意の整数)までの総和をとるプログラムでも書いてみよーと思いました。

protected override void SolveInstance(IGH_DataAccess DA)
{
  DA.GetData("num", ref num);
  int[] values = new int[num];
  for (int i = 0; i < num; i++)
  {
    values[i] = i + 1;
  }

  int sum = 0;
  Parallel.For(0, num, i =>
  {
    sum += values[i];
  });
  DA.SetData("sum", sum);
}

1から10000の総和が28571331?

Why ?

みたいなことがおきました。

原因と解決策

並列計算はCPUの複数のスレッドを利用しています。なのにcode中のsumという変数は一つ。つまり、別々のスレッドからこの変数に同時にアクセスが起きたりしてしまうわけですね。

なので、スレッド共通の変数に書き込んでいる間はほかのスレッドからの書込みを待ってもらわないといけません。(これを排他処理と呼ぶらしいです)

排他処理を書いたコードがこちら!

protected override void SolveInstance(IGH_DataAccess DA)
{
  DA.GetData("num", ref num);
  int[] values = new int[num];
  for (int i = 0; i < num; i++)
  {
    values[i] = i + 1;
  }

 object lockObject = new object();
  int sum = 0;
  Parallel.For(0, num, i =>
  {
    lock(lockObject){ sum += values[i]; }
  });
  DA.SetData("sum", sum);
}

こんな感じで書けばうまく動きました。寝る前に少し勉強しようと思ったら、めちゃくちゃ時間かかってしまったwww

まとめ

並列計算をするときは共通の変数がないかを常に意識する!

コメント

タイトルとURLをコピーしました