マルチスレッドをC#で実装してみよう
スレッドkとはプログラムを実行する処理の最小単位です。
アプリはメインスレッドといわれる単一のスレッド(シングルスレッド)で動作しています。
Mainスレッドで始まるスレッドがそうです。
シングルスレッドでは、通信が終わるまで他の操作はできません。
そこで出てくるのがマルチスレッドというものです。
マルチスレッドを利用することで、ネットワーク通信のように時間のかかる処理はバックグラウンドで実施し、アプリ利用者はメインスレッド上で操作を継続で気できるようになります。
ちなみに、スレッドと同じく処理単位を表す概念として、プロセスもあります。
相互の関係としては、一つのプロセスに対して、一つ以上のスレッドが属するという関係です。
プロセスはいわゆるプログラムのインスタンスそのものであり、新たなプロセスの動作にはCPUとメモリの割り当てが必要になります。その性質上、独立したメモリ空間を必要としない状況ではメモリの消費効率がよくありません。
それに対して、スレッドとはメモリ空間を共有しながら、処理だけを分離する仕組みともいえるため、その性質上メモリの消費効率は良くなりますが、反面。スレッド間でデータを共有している場合に、同時アクセス(競合)を意識しなければなりません。
新たなスレッドを生成するクラシカルな方法として、まずThreadクラスを地用することです。
しかし、後から述べる理由から現在は利用すべきではありません。
あくまで、原始的なスレッド生成実行の方法です。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MultiThread
{
class Program
{
static void Main(string[] args)
{
var t1 = new Thread(Count);
var t2 = new Thread(Count);
var t3 = new Thread(Count);
t1.Start(1);
t2.Start(2);
t3.Start(3);
t1.Join();
t2.Join();
t3.Join();
Console.WriteLine("すべての処理が完了しました");
}
static void Count(object n)
{
for (int i = 0;i < 50; i++)
{
Console.WriteLine($"Thread{n}:{i}");
}
}
}
}
スレッドの生成実行はプロセスに比べればオーバーヘッドの小さな処理です。
しかし、通常の命令に比べると十分に重たい処理であり、スレッドの生成破棄を繰り返すことはアプリ全体のパフォーマンスを劣化させます。
そこで、いったん生成したスレッドを破棄せずに保持しておいて、別な処理で再利用する仕組みが導入されました。
それがスレッドプールです。
スレッドプールを利用することで、スレッドを大量に利用するようなアプリでもスレッドの生成破棄のオーバーヘッドを軽減できます。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MultiThread
{
class Program
{
static void Main(string[] args)
{
Task t1 = Task.Run(() => Count(1));
Task t2 = Task.Run(() => Count(2));
Task t3 = Task.Run(() => Count(3));
t1.Wait();
t2.Wait();
t3.Wait();
Console.WriteLine("すべての処理が完了しました");
}
static void Count(object n)
{
for (int i = 0;i < 50; i++)
{
Console.WriteLine($"Thread{n}:{i}");
}
}
}
}