C#でプログラムを作成する際、「処理が重くてアプリが固まる」といった経験をしたことはありませんか?
そんなときに活躍するのが「スレッド(Thread)」です。
スレッドを使えば、複数の処理を同時に実行できるため、ユーザーにとってストレスのない操作感を実現できます。
この記事では、C#におけるスレッドの基本的な使い方から、実践的なサンプルコードまでを詳しく解説します。
マルチスレッドに初めて触れる方でも理解しやすいよう、順を追って説明していきます。
スレッドとは、プロセス内で動作する「軽量な実行単位」のことです。
通常、C#アプリケーションは1つのスレッド(メインスレッド)で動作しますが、
重い処理をメインスレッドに任せてしまうと、UIが固まるなどの問題が発生します。
このような問題を解決するために、処理を別のスレッドで実行することが重要になります。
スレッドを使うことで、非同期的に複数の処理を実行でき、アプリケーション全体の応答性が向上します。
C#では、System.Threading.Thread
クラスを使ってスレッドを作成できます。
まずは最も基本的なスレッドの作り方を見てみましょう。
using System;
using System.Threading;
class Program
{
static void Main()
{
Thread t = new Thread(new ThreadStart(Run));
t.Start();
Console.WriteLine("メインスレッド終了");
}
static void Run()
{
Console.WriteLine("別スレッドで実行中");
}
}
Thread
クラスを使って新しいスレッドを作成。ThreadStart
デリゲートに、実行したいメソッドを指定。Start()
でスレッドの実行を開始。引数を渡したい場合には、ParameterizedThreadStart
を使用します。
static void Main()
{
Thread t = new Thread(new ParameterizedThreadStart(RunWithParameter));
t.Start("こんにちは、スレッド!");
}
static void RunWithParameter(object message)
{
Console.WriteLine("メッセージ: " + message);
}
object
型で渡すため、キャストが必要な場合があります。スレッド制御には以下のようなメソッドやプロパティがあります。
Thread t = new Thread(() =>
{
Console.WriteLine("開始");
Thread.Sleep(2000); // 2秒停止
Console.WriteLine("終了");
});
t.Start();
t.Join(); // スレッドの終了を待つ
Console.WriteLine("すべての処理が終了しました");
Sleep(ms)
:指定時間スレッドを停止。Join()
:そのスレッドが終わるまで待機。IsAlive
:スレッドがまだ動いているかどうか。UIアプリケーションでは、UI部品の更新はメインスレッドのみが許可されています。
サブスレッドから直接UIを更新すると例外が発生するため、Invoke
やDispatcher
を使ってメインスレッドに処理を渡す必要があります。
this.Invoke((MethodInvoker)(() =>
{
label1.Text = "更新されました";
}));
Dispatcher.Invoke(() =>
{
label1.Content = "更新されました";
});
.NETでは、より扱いやすい非同期プログラミングのためにTask
クラスが用意されています。
Task.Run(() =>
{
Console.WriteLine("非同期タスクで処理中");
});
async/await
構文との連携が可能。実際の開発では、Taskやasync/awaitの方が推奨されるケースが多いです。
複数のスレッドが同じデータにアクセスする場合、データ破損を防ぐために排他制御が必要です。
private static object lockObj = new object();
static void SafeMethod()
{
lock (lockObj)
{
// 共有資源へのアクセス
Console.WriteLine("ロック中の処理");
}
}
lock
キーワードで排他制御を行います。例えばファイルの読み込みやデータベースの検索など、重い処理は別スレッドで行うのが定石です。
Thread backgroundThread = new Thread(() =>
{
// 時間のかかる処理
Thread.Sleep(5000);
Console.WriteLine("処理完了");
});
backgroundThread.IsBackground = true;
backgroundThread.Start();
IsBackground = true
とすることで、メインスレッド終了時にバックグラウンドスレッドも終了します。C#でスレッドを使うことで、アプリケーションの処理を効率的に分担し、ユーザーのストレスを減らすことができます。
ただし、スレッドは便利である一方、状態管理や排他制御をしっかり行わないと、バグやデッドロックの原因になります。
最近では、Taskやasync/awaitといった非同期処理が主流になりつつありますが、
スレッドの仕組みを理解しておくことは、より高度なプログラム開発の基礎として重要です。