Task Parallel Library (TPL) を使う

TPLとは

C#などの.NET Framework上の言語で並列処理を行うライブラリです。最近のCPUはほぼすべてマルチコアな訳で、それなのにシングルスレッドでプログラムを書いてCPU1個しか働いてないのはもったいない、全部働かせよう、というわけです。自分でThreadなどを使ってプログラムすることも可能ではありますが、ひどく面倒になるのでそれを楽にできるのがTPLです。

ダウンロード

以下のページからダウンロードできます。
http://www.microsoft.com/downloads/details.aspx?FamilyId=348F73FD-593D-4B3C-B055-694C50D2B0F3&displaylang=en

準備

で、なんでこんな記事を書こうと思ったかといえば、どこの名前空間を使えばいいかなかなか分からなかったためです。これは忘れるとまた困ると思い備忘録にしておきます。
結論から言えば、System.Threading名前空間でした。これは元からありますが、それが拡張される形になります。使うためには、VisualStudioの「参照の追加」で System.Threading を追加します。

Parallel.For

並列実行モデルは大きく「parallel-for」モデルと「fork-join」モデルに大別されます。最初はparallel-forから。

これはものすごく時間がかかるようなfor文を並列実行しようというものです。
例えばこのようなfor文は、

for(int i=0; i<100; i++){
    // 何かの処理
}

TPLのParallelクラスを用いて並列化版にするとこのようになります。

Parallel.For(0, 100, 1, delegate(int i){
    // 何かの処理
});

こんな例で実行時間を試してみました。20000回ビットマップを作らせる勝負です。

const int COUNT = 20000;
Stopwatch watch = new Stopwatch();

// 普通のfor
watch.Start();
{
    for (int i = 0; i < COUNT; i++) {
        using (Bitmap bitmap = new Bitmap(640, 480)) { }
    }
}
watch.Stop();
Console.WriteLine("普通のfor\t: {0}ms", watch.ElapsedMilliseconds);

// Parallel.For
watch.Reset();
watch.Start();
{
    Parallel.For(0, COUNT, 1, delegate(int i) {
        using (Bitmap bitmap = new Bitmap(640, 480)) { }
    });
}
watch.Stop();
Console.WriteLine("Parallel.For\t: {0}ms", watch.ElapsedMilliseconds);

当方のPC(Core2 Duo E8400 3GHz)での実行結果は、以下のようになりました。

普通のfor     : 21292ms
Parallel.For  : 18924ms

若干勝ってます。なお、結構重い処理でないと効果は出てきません。ちょっとしたfor文だとかえって遅くなります。

Parallel.Invoke

もう一つのfork-joinに当たるのが、多分Parallel.Invokeメソッドです。指定した複数のデリゲートを同時実行します。全てのデリゲートが完了したら、処理が戻ってきます。

Parallel.Invoke(
    delegate{ Hoge(); },
    delegate{ Fuga(); }
);

2分木をたどっていく場合のような、処理がいくつかの独立した部分に分けられそうな時には有効そうです。