連載:C# 4入門

第3回 TaskクラスとPLINQ(Parallel LINQ)

株式会社ピーデー 川俣 晶
2010/09/17
Page1 Page2 Page3

待てといわれて待つヤツがいるぞ

 Taskクラスでは複数の処理を実行することができる。その場合、知りたいタイミングは恐らく2つだ。1つは、1つでもタスクが終了したとき。もう1つは、すべてのタスクが終了したときだろう。これも簡単に判定できる。

 Task.WaitAnyメソッドとTask.WaitAllメソッドは引数にTaskオブジェクトを列挙でき、それぞれ1つでも終わったとき、すべて終わったときに戻ってくる機能を持つ。これを使えば、複数のタスクを作って終了を待つ場合でも、簡単にコードを書ける。

using System;
using System.Threading.Tasks;

class Program
{
  static void Main(string[] args)
  {
    var task1 = Task.Factory.StartNew(() =>
      {
        for (int i = 0; i < 100; i++) Console.Write('A');
      });
    var task2 = Task.Factory.StartNew(() =>
      {
        for (int i = 0; i < 200; i++) Console.Write('B');
      });
    var task3 = Task.Factory.StartNew(() =>
      {
        for (int i = 0; i < 300; i++) Console.Write('C');
      });

    Task.WaitAny(task1, task2, task3);
    Console.WriteLine();
    Console.WriteLine("stopped one task");

    Task.WaitAll(task1, task2, task3);
    Console.WriteLine();
    Console.WriteLine("stopped all tasks");
  }
}
リスト3
task3.cs

ABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
stopped one task
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
BBBBBBBBBBBBBBB
stopped all task
リスト3の実行結果
環境やタイミングに依存する。

 さて、問題はその先である。Task.WaitAnyメソッドとTask.WaitAllメソッドは便利だが、それでもスレッド関係に比べると機能が少ない。また、いくら簡単になったといっても、同期の問題は丸ごと残る。別のタスクから同じリソースを使うときは、やはりlockステートメントなどを使わねばならない。果たして、それにもかかわらずタスクを使うべきなのだろうか?

 答えは「使うべきである」だ。なぜなら、「気軽に使えるハードルの低さ」こそがTaskクラスの身上であり、それを使うことがメニー・コア時代に対応する1つの処方箋だからである。いくら同期の問題が……といっても、それが問題にならないケースや、問題になっても軽いこともある。常にヘビーなソース・コードを想定する必要はない。それよりも、並列実行できる処理を別タスクに分散させる習慣を付ける方が、よほど重要だろう。それが複数コアを持つ未来型CPUのパワーを引き出す早道だからである。タスクの終了は待ってくれるが、時代は待ってくれないのである。

PLINQ(Parallel Linq)

 一見、並列処理の目玉に見えるPLINQだが、後回しにしたのは結局扱いがおどろくほど簡単だからにすぎない。

 まず、クエリ式を含む簡単なソースを作成した。プログラムそのものには意味がない。「2」を探して、それを出力するだけである。

using System;
using System.Linq;
using System.Threading.Tasks;

class Program
{
  static void Main(string[] args)
  {
    int[] ar = { 1, 2, 3 };

    var q1 = from n in ar
             where n == 2
             select n;

    foreach (var n in q1)
      Console.WriteLine("found {0}", n);
  }
}
リスト4

found 2
リスト4の実行結果

 これをパラレル(並列)対応するには、クエリ式にAsParallelメソッドを追加するだけである。

var q1 = from n in ar.AsParallel()
         where n == 2
         select n;

 メソッド形式でも同じである。例えば、こういうクエリがあったとしよう。

var q1 = ar.Where((c) => c == 2);

 これは以下のように、メソッド・チェーンの最初にAsParallelメソッドを挟むだけである。

var q1 = ar.AsParallel().Where((c) => c == 2);

 がく然とするほど簡単である。これといって追加のトピックもない。メソッド1つでクエリがパラレル化されるのである。しかし、これで有り難みが大きいかといえば、必ずしもそうではない。というのは、以下のような状況が存在するからである。

  • 別のサーバにクエリを投げる場合など、クエリを受け取って実行する側の性能に依存する要素が大きい(ローカル側のパラレルの問題ではない。PLINQは基本的にローカル側で処理されるLinq to Objects用の機能。Linq to SQLなどで有効というわけではない)
  • 湯水のごとく大量に平然とクエリ式を消費するようになると、ほとんどのケースでクエリ式はボトルネックではなく、いちいちAsParallelメソッドを挿入してもあまりメリットはなく、むしろ手間がかかり、ソース・コードの可読性も落ちてしまう
  • AsParallelメソッドを入れると、結果が変わってしまう場合がある

 問題は最後の1つである。いったい何が変わってしまうのだろうか。


 INDEX
  C# 4入門
  第3回 TaskクラスとPLINQ(Parallel LINQ)
    1.天は自らタスクるものをタスク/Taskクラスの基本的な使い方
  2.待てといわれて待つヤツがいるぞ/PLINQ(Parallel Linq)
    3.PLINQのワナ/まとめ
 
インデックス・ページヘ  「C# 4入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間