C# マルチスレッド

マルチスレッドとは、複数のスレッドが同時に動作するプロセスです。スレッドをプログラムの実行パスとみなすことができます。各スレッドは、特定のタスクを完了するための独自の制御フローを定義します。アプリケーションに複雑で時間のかかる操作が含まれる場合、複数のスレッドを使用して操作を実行すると有益な場合があります。マルチスレッドを使用すると、CPU リソースを節約し、アプリケーションの実行効率を向上させることができます。たとえば、最新のオペレーティング システムでの同時プログラミングの実装では、マルチスレッドが使用されます。これまでに作成したサンプル プログラムは、一度に 1 つのタスクを実行するシングルスレッド アプリケーションでした。

スレッドのライフサイクル

スレッドのライフサイクルは、System.Threading.Thread クラス オブジェクトを作成したときに開始され、スレッドが終了するか実行が完了したときに終了します。

スレッドのライフサイクルのさまざまな状態を以下に示します。

  • 未開始状態: スレッド インスタンスが作成されたが、Start メソッドが呼び出されていないときの状態。
  • 準備完了状態: スレッドの実行準備が整い、CPU サイクルを待っている状態。
  • 実行不可能な状態: 次の状況では、スレッドは実行できません。
    • Sleep メソッドが呼び出されています。
    • Wait メソッドが呼び出されています。
    • I/O 操作によるブロック。
  • デッド状態: スレッドが実行を完了したか、中止された状態。

メインスレッド

C# では、System.Threading.Thread クラスを使用してスレッドを処理します。これにより、マルチスレッド アプリケーションで個々のスレッドの作成とアクセスが可能になります。マルチスレッドで実行される最初のスレッドはメイン スレッドと呼ばれます。C# プログラムの実行が開始されると、メイン スレッドが自動的に作成され、Thread クラスを使用して作成されたスレッドは子スレッドと呼ばれます。スレッドにアクセスするためのスレッドクラス。以下は、サンプル プログラムによるメイン スレッドの実行を示しています。

use System;
use System.Threading;

namespace it-kiso.com
{
    クラス Demo
    {
        static void Main(string[] args)
        {
            Thread th = Thread.CurrentThread;
            th.Name = "Thread Name";
            Console.WriteLine("これは{0}です", th.Name);
            Console.ReadKey();
        }
    }
} 

操作の結果は次のようになります。

これはThread Nameです

Thread クラスのプロパティとメソッド

次の表に、Thread クラスでよく使用されるプロパティをいくつか示します。

属性 説明
CurrentContext スレッドが実行されているコンテキストを取得します
CurrentCulture 現在のスレッドのカルチャを取得または設定します
CurrentPrincipal スレッドの現在のリーダーを取得または設定します (ロールベースのセキュリティの場合)
CurrentThread 現在実行中のスレッドを取得する
CurrentUICulture 実行時にカルチャ固有のリソースを検索するためにリソース マネージャーによって使用される現在のカルチャを取得または設定します
ExecutionContext 現在のスレッドのさまざまなコンテキストに関する情報を含む ExecutionContext オブジェクトを取得します。
IsAlive 現在のスレッドの実行ステータスを取得します
IsBackground スレッドがバックグラウンド スレッドであるかどうかを示す値を取得または設定します
IsThreadPoolThread スレッドがマネージド スレッド プールに属しているかどうかを取得します
ManagedThreadId 現在管理されているスレッドの一意の識別子を取得します
Name スレッドの名前を取得または設定します
Priority スレッドのスケジュール優先度を取得または設定します
ThreadState 現在のスレッドの状態を取得します

次の表に、Thread クラスの一般的に使用されるメソッドをいくつか示します。

メソッド名 説明
public void Abort() このメソッドを呼び出しているスレッドで ThreadAbortException をスローして、このスレッドを終了します
public static LocalDataStoreSlot AllocateDataSlot() すべてのスレッドに名前のないデータ スロットを割り当てます。パフォーマンスを向上させるために、代わりに ThreadStaticAttribute 属性でマークされたフィールドを使用してください。
public static LocalDataStoreSlot AllocateNamedDataSlot(string name) すべてのスレッドに名前付きデータ スロットを割り当てます。パフォーマンスを向上させるには、代わりに ThreadStaticAttribute 属性でマークされたフィールドを使用します。
public static void BeginCriticalRegion() スレッドの中止または未処理の例外の影響により、アプリケーション ドメイン内の他のタスクが危険にさらされる可能性があるコード領域に実行が進もうとしていることをホストに通知します。
public static void BeginThreadAffinity() 現在の物理オペレーティング システム スレッドに依存する特定の命令をマネージド コードが実行しようとしていることをホストに通知します。
public static void EndCriticalRegion() スレッドの中止または未処理の例外が現在のタスクにのみ影響するコード領域に実行が進もうとしていることをホストに通知します。
public static void EndThreadAffinity() 現在の物理オペレーティング システム スレッドに依存する特定の命令の実行をマネージ コードが終了したことをホストに通知します。
public static void FreeNamedDataSlot(string name) プロセス内のすべてのスレッドのデータ スロットから名前の関連付けを解除します。パフォーマンスを向上させるには、代わりに ThreadStaticAttribute 属性でマークされたフィールドを使用してください。
public static Object GetData(LocalDataStoreSlot slot) 現在のスレッドで指定された値を取得します。パフォーマンスを向上させるには、代わりに ThreadStaticAttribute 属性でマークされたフィールドを使用してください。
public static AppDomain GetDomain() 現在のスレッドが実行されているドメインを返します。
public static AppDomain GetDomainID() アプリケーションドメインの一意の識別子を返します。
public static LocalDataStoreSlot GetNamedDataSlot(string name) 名前付きデータ スロットを見つけます。パフォーマンスを向上させるには、代わりに ThreadStaticAttribute 属性でマークされたフィールドを使用してください。
public void Interrupt() WaitSleepJoin 状態のスレッドを中断する
public void Join() 標準の COM および SendMessage メッセージのポンピングが継続している間、スレッドが終了するまで呼び出し元のスレッドをブロックします。このメソッドにはさまざまなオーバーロード形式があります
public static void MemoryBarrier() メモリ アクセスを次のように同期します。現在のスレッドを実行しているプロセッサは、最初に MemoryBarrier 呼び出しの後にメモリ アクセスを実行し、次に MemoryBarrier 呼び出しの前にメモリ アクセスを実行することによって命令を並べ替えることはできません。
public static void ResetAbort() 現在のスレッドに対して要求された中止をキャンセルします。
public static void SetData(LocalDataStoreSlot slot, Object data) このスレッドの現在のドメインで現在実行中のスレッド上の指定されたスロットにデータを設定します。パフォーマンスを向上させるには、代わりに ThreadStaticAttribute 属性でマークされたフィールドを使用してください。
public void Start() スレッドを開始する
public static void Sleep(int millisecondsTimeout) スレッドを一定期間停止します
public static void SpinWait(int iterations) スレッドを一定期間待機させます。時間の長さは iterations パラメータによって定義されます。
public static byte VolatileRead(ref byte address)
public static double VolatileRead(ref double address)
public static int VolatileRead(ref int address)
public static Object VolatileRead(ref Object address)
フィールド値を読み取ります。プロセッサの数やプロセッサ キャッシュの状態に関係なく、値はコンピュータのプロセッサによって書き込まれた最新の値です。
public static void VolatileWrite(ref byte address, byte value)
public static void VolatileWrite(ref double address, double value)
public static void VolatileWrite(ref int address, int value)
public static void VolatileWrite(ref Object address, Object value)
フィールドに値を即座に書き込み、その値がコンピュータ内のすべてのプロセッサに表示されるようにします。
public static bool Yield() 現在呼び出しているスレッドを終了し、実行準備ができている別のスレッド (オペレーティング システムによって実行用に選択された別のスレッド) を実行します。

スレッドを作成する

C# は、Thread クラスを拡張してスレッドを作成し、拡張された Thread クラスを使用して Start() メソッドを呼び出し、子スレッドの実行を開始します。以下は、サンプル プログラムによるスレッドの作成を示しています。

using System;
using System.Threading;

namespace it-kiso.com
{
    class Demo
    {
        public static void CallToChildThread()
        {
            Console.WriteLine("子スレッドが実行されています");
        }
      
        static void Main(string[] args)
        {
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("Main関数で子スレッドを作成します");
            Thread childThread = new Thread(childref);
            childThread.Start();
            Console.ReadKey();
        }
    }
} 

操作の結果は次のようになります。

Main関数で子スレッドを作成します
子スレッドが実行されています

スレッドを管理する

Thread クラスには、スレッドを管理するためのさまざまなメソッドが用意されています。たとえば、次の例に示すように、sleep() メソッドを使用して、特定の期間スレッドを一時停止できます。

using System;
using System.Threading;

namespace it-kiso.com
{
    class Demo
    {
        public static void CallToChildThread()
        {
            Console.WriteLine("サブスレッドを実行します。");
            // スレッドを5000ミリ秒一時停止します。
            int sleepfor = 5000;
            Console.WriteLine("サブスレッドは{0}秒休止します。", sleepfor / 1000);
            Thread.Sleep(sleepfor);
            Console.WriteLine("サブスレッドを再開します。");
        }
      
        static void Main(string[] args)
        {
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("メイン関数でサブスレッドを作成します。");
            Thread childThread = new Thread(childref);
            childThread.Start();
            Console.ReadKey();
        }
    }
} 

スレッドを破壊する

Abort() メソッドは、スレッドを破棄するために Thread クラスで提供されます。Abort() メソッドは、スレッドを終了するために threadabortException 例外をスローします。次の例に示すように、この例外はキャッチできません。

using System;
using System.Threading;

namespace it-kiso.com
{
    class Demo
    {
        public static void CallToChildThread()
        {
            try{
                Console.WriteLine("子スレッドを実行");
                // 10までカウント
                for (int counter = 0; counter <= 10; counter++)
                {
                    Thread.Sleep(500);
                    Console.WriteLine(counter);
                }
                Console.WriteLine("子スレッドが完了しました");

            }catch (ThreadAbortException e){
                Console.WriteLine("スレッド中断:{0}", e);
            }finally{
                Console.WriteLine("スレッド例外をキャッチできません");
            }
        }
        static void Main(string[] args)
        {
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("Main関数内で子スレッドを作成");
            Thread childThread = new Thread(childref);
            childThread.Start();
            // メインスレッドを数秒停止
            Thread.Sleep(2000);
            // 子スレッドを中止
            Console.WriteLine("Main関数内で子スレッドを中止");
            childThread.Abort();
            Console.ReadKey();
        }
    }
}