C#のParallelクラスを使った並列処理の基本を学ぼう
生徒
「先生、たくさんのデータを処理するとき、パソコンの力をフルに使ってもっと速く終わらせる方法はありますか?」
先生
「それは素晴らしい視点ですね!C#には『Parallel(パラレル)クラス』という、複数の処理を同時に進めるための便利な道具がありますよ。」
生徒
「同時に進める……。一人でやる作業を、みんなで分担するようなイメージでしょうか?」
先生
「まさにその通りです!今回は、その『並列処理』の基本について、初心者の方にも分かりやすく解説していきますね。」
1. Parallelクラスと並列処理とは?
プログラミングにおける並列処理(へいれつしょり)とは、複数の作業を「同時」に行うことを指します。通常、プログラムは上から下へ、一つずつ順番に命令をこなしていきます。これを「逐次処理(ちくじしょり)」と呼びます。
しかし、最近のパソコンには「CPU(シーピーユー)」の中に「コア」と呼ばれる、計算を行うための頭脳が複数入っています。例えば、4つのコアがあるパソコンなら、理論上は4つの作業を同時に進めることができるのです。
C#のParallelクラスは、この複数の頭脳を簡単に使いこなすための仕組みを提供してくれます。これを使うことで、大量の計算やデータの加工にかかる時間を大幅に短縮できる可能性があります。
100枚の皿洗いをイメージしてください。
- 逐次処理: 1人の人が、1枚ずつ順番に100枚洗います。
- 並列処理(Parallel): 4人の人が、25枚ずつ手分けして同時に洗います。
2. 実際にParallel.Forを使ってみよう
まずは、最も基本的なParallel.Forという書き方を紹介します。これは、普通の「for文(繰り返し処理)」を並列化したものです。まずは、普通の繰り返し処理を見てみましょう。
// 普通の繰り返し処理(逐次処理)
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"現在 {i} 番目の処理を実行中です。");
}
これを、Parallelクラスを使って書き換えると、次のようになります。
using System;
using System.Threading.Tasks; // Parallelを使うために必要です
class Program
{
static void Main()
{
// Parallel.Forを使った並列処理
Parallel.For(0, 10, i =>
{
Console.WriteLine($"並列処理中: {i}");
});
}
}
実行結果は、次のようにバラバラな順番で表示されるはずです。
並列処理中: 0
並列処理中: 2
並列処理中: 4
並列処理中: 1
並列処理中: 3
...(順不同で表示される)
解説:
普通のfor文は、必ず0, 1, 2...と順番に進みます。しかし、Parallel.Forは「空いている頭脳」に仕事をどんどん割り振るため、どの番号が先に終わるかは分かりません。これが並列処理の大きな特徴です。
3. データの集まりを処理する Parallel.ForEach
次に、リストや配列などのデータの集まりを処理するのに便利なParallel.ForEachを紹介します。これは、データの数だけ「分身」を作って一斉に作業をさせるイメージです。
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
class Program
{
static void Main()
{
// 処理したいデータのリスト
List<string> fruits = new List<string> { "りんご", "みかん", "バナナ", "ぶどう", "いちご" };
// リストの中身を並列で処理する
Parallel.ForEach(fruits, fruit =>
{
Console.WriteLine($"{fruit} の加工が完了しました。");
});
}
}
ここでも、出力される順番は実行するたびに変わります。foreach(フォーイーチ)という言葉は、「それぞれに対して」という意味があり、リストの中の各アイテムに対して並列に処理を実行してくれます。
4. 並列処理を使うときの注意点と用語解説
並列処理はとても強力ですが、いくつか気をつけなければならないポイントがあります。ここで重要な用語と一緒に確認しておきましょう。
スレッド(Thread)
プログラムが動く際の「作業単位」のことです。並列処理は、複数のスレッドを立ち上げて同時に作業を進める技術です。Parallelクラスは、このスレッドの管理を自動で行ってくれるとても賢いクラスです。
オーバーヘッド(Overhead)
作業を分担するための「準備時間」のことです。例えば、たった3枚の皿を洗うのに、わざわざ4人を集めて説明して……とやっていると、逆に時間がかかってしまいます。データが少ない場合は、普通のfor文の方が速いこともあります。
スレッドセーフ(Thread Safe)
複数のスレッドから同時に一つのデータ(例えば変数の値など)を書き換えると、計算がおかしくなることがあります。これを防ぐことを「スレッドセーフにする」と言います。並列処理の中では、同じ変数を奪い合わないように設計することが大切です。
5. 複数の異なる作業を同時に行う Parallel.Invoke
これまでは「同じような作業をたくさん繰り返す」例でしたが、Parallel.Invokeを使うと「全く別の作業Aと作業Bを同時に行う」ことができます。
using System;
using System.Threading.Tasks;
class Program
{
static void Main()
{
// 異なるメソッド(処理の塊)を同時に実行
Parallel.Invoke(
() => DoWorkA(),
() => DoWorkB()
);
}
static void DoWorkA()
{
Console.WriteLine("作業Aを開始します...");
// 何か重い処理
}
static void DoWorkB()
{
Console.WriteLine("作業Bを開始します...");
// 何か別の重い処理
}
}
このように書くと、作業Aが終わるのを待たずに作業Bが動き出します。全体の待ち時間を減らすことができる便利な機能です。
6. 並列処理が適している場面
どのようなときにParallelクラスを使うべきでしょうか?主なケースを挙げてみます。
- 大量の画像処理: 1,000枚の画像のリサイズを同時に行う。
- 複雑な計算: 膨大な数値シミュレーションを分割して計算する。
- ファイルの読み込み: 複数の独立したファイルを同時に読み込む。
逆に、前の計算結果を使って次の計算をするような「順番が重要な処理」には並列処理は向きません。作業がそれぞれ独立しているかどうかが、導入の判断基準になります。
7. Parallelクラスの仕組み「タスク並列ライブラリ(TPL)」
少し難しい名前ですが、Parallelクラスは「TPL(Task Parallel Library)」という巨大な仕組みの一部です。これは、開発者が「スレッド」という細かい部品を直接触らなくても、安全に効率よく並列処理ができるようにMicrosoftが用意してくれたものです。初心者の方は、「Parallelという名前の通り、並行して走らせるための便利なセット」と覚えておけば十分です。
C#というプログラミング言語は、こうした最新のパソコンの性能を最大限に引き出すための仕組みが非常に充実しています。まずはParallel.Forから試してみて、自分の書いたプログラムが高速に動く感動を味わってみてください!