English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

C# マルチスレッド

スレッド プログラムの実行パスとして定義されます。各スレッドは独自の制御フローを定義します。アプリケーションが複雑で時間がかかる操作に関与する場合、異なるスレッドの実行パスを設定することは有益です。各スレッドが特定の作業を実行します。

スレッドは軽量プロセススレッドを使用する一般的な例は、現代のオペレーティングシステムで並行プログラミングを実装することです。スレッドを使用することでCPUサイクルの無駄を節約し、アプリケーションの効率を向上させます。

今までに書いたプログラムは、アプリケーションの実行例としての単一のプロセスとして動作する単一のスレッドです。しかし、複数のタスクを同時に実行するために、それがより小さなスレッドに分割されることができます。

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

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

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

  • 未始動状態:スレッドの例が作成されましたが Start メソッドが呼び出されていない状態です。

  • 就熟状態:スレッドが実行準備ができて CPU 周期を待っている状態です。

  • 実行不可能状態以下のいくつかの状況では、スレッドは実行不可能です:

    • Sleep メソッドが呼び出されました

    • Wait メソッドが呼び出されました

    • I/O 操作でブロックされました

  • 死亡状態:スレッドが実行を完了したり中止された場合の状態です。

主スレッド

C# では、System.Threading.Thread クラスは、スレッドの作業に使用されます。それは、マルチスレッドアプリケーションの中で単一のスレッドを作成しアクセスを許可します。プロセスで最初に実行されるスレッドは、主スレッド

C# プログラムが実行されると、自動的に主スレッドが作成されます。以下を使用して、 Thread クラスが作成したスレッドは、主スレッドのサブスレッドによって呼び出されます。Thread クラスを使用して、 CurrentThread 属性がスレッドにアクセスします。

以下のプログラムは、主スレッドの実行を示しています:

using System;
using System.Threading;
namespace MultithreadingApplication
{
    class MainThreadProgram
    {
        static void Main(string[] args)
        {
            Thread th = Thread.CurrentThread;
            th.Name = "MainThread";
            Console.WriteLine("This is {0}", th.Name);
            Console.ReadKey();
        }
    }
}

上記のコードがコンパイルおよび実行された場合、以下のような結果が得られます:

This is MainThread

Thread クラスでよく使用される属性とメソッド

以下のテーブルに示す Thread クラスの一般的な 属性

属性説明
CurrentContextスレッドがその中で実行している現在の上下文を取得します。
CurrentCulture現在のスレッドの地域を取得または設定します。
CurrentPrincipalスレッドの現在の責任者(ロールベースのセキュリティに対して)を取得または設定します。
CurrentThread現在実行中のスレッドを取得します。
CurrentUICulture実行時に地域特定のリソースを検索するために、現在の地域を取得または設定します。
ExecutionContext現在のスレッドに関するさまざまな上下文情報を含む ExecutionContext オブジェクトを取得します。
IsAlive現在のスレッドの実行状態を示す値を取得します。
IsBackgroundバックグラウンドスレッドであるかどうかを示す値を取得または設定します。
IsThreadPoolThreadスレッドがマネージドスレッドプールに属しているかどうかを示す値を取得または設定します。
ManagedThreadId現在のマネージドスレッドのユニークな識別子を取得します。
Nameスレッドの名前を取得または設定します。
Priorityスレッドのスケジューリング優先度を取得または設定します。
ThreadState現在のスレッドの状態を含む値を取得します。

以下のテーブルに示す Thread クラスの一般的な メソッド

序号メソッド名 & 説明
1public void Abort()
このメソッドを呼び出すスレッドでThreadAbortExceptionが発生し、このスレッドの終了プロセスを開始します。このメソッドを呼び出すと、通常スレッドが終了します。
2public static LocalDataStoreSlot AllocateDataSlot()
すべてのスレッドに無名データスロットを割り当てます。より良いパフォーマンスを得るために、ThreadStaticAttribute属性でマークされたフィールドを使用することをお勧めします。
3public static LocalDataStoreSlot AllocateNamedDataSlot( string name)
すべてのスレッドに名前付きデータスロットを割り当てます。より良いパフォーマンスを得るために、ThreadStaticAttribute属性でマークされたフィールドを使用することをお勧めします。
4public static void BeginCriticalRegion()
ホストが次にコード領域に入ると通知します。そのコード領域内でスレッドが中止されたり、未処理の例外が発生すると、アプリケーションドメイン内の他のタスクに影響を与える可能性があります。
5public static void BeginThreadAffinity()
ホストコードが現在の物理オペレーティングシステムスレッドの識別子に依存するコマンドを実行しようとします。
6public static void EndCriticalRegion()
ホストが次にコード領域に入ると通知します。そのコード領域内でスレッドが中止されたり、未処理の例外が発生すると、現在のタスクにのみ影響を与えます。
7public static void EndThreadAffinity()
ホストコードが現在の物理オペレーティングシステムスレッドの識別子に依存するコマンドを実行したことを通知します。
8public static void FreeNamedDataSlot(string name)
プロセス中のすべてのスレッドで名前とスロットの関連を解除します。パフォーマンスを向上させるために、ThreadStaticAttribute 属性でマークされたフィールドを使用することをお勧めします。
9public static Object GetData( LocalDataStoreSlot slot )
現在のスレッドの現在のドメインで、現在のスレッド上の指定されたスロットから値を検索します。パフォーマンスを向上させるために、ThreadStaticAttribute 属性でマークされたフィールドを使用することをお勧めします。
10public static AppDomain GetDomain()
現在のスレッドがその中で実行している現在のドメインを返します。
11public static AppDomain GetDomainID()
ユニークなアプリケーションドメイン識別子を返します。
12public static LocalDataStoreSlot GetNamedDataSlot( string name )
名前付きデータスロットを検索します。パフォーマンスを向上させるために、ThreadStaticAttribute 属性でマークされたフィールドを使用することをお勧めします。
13public void Interrupt()
WaitSleepJoin スレッド状態にあるスレッドを中断します。
14public void Join()
標準の COM と SendMessage メッセージプール処理の実行中に、呼び出しスレッドをブロックし、あるスレッドが終了するまで待機します。このメソッドには異なるオーバーロードがあります。
15public static void MemoryBarrier()
メモリアクセスの同期方法は以下の通りです:現在のスレッドのプロセッサがインストラクションの再順序付けを行う際には、MemoryBarrier 請求の後のメモリアクセスを行い、MemoryBarrier 請求の前のメモリアクセスを行う順序で実行しないでください。
16public static void ResetAbort()
現在のスレッドにリクエストされた Abort をキャンセルします。
17public static void SetData( LocalDataStoreSlot slot, Object data )
現在実行中のスレッド上で、このスレッドの現在のドメインを指定されたスロットでデータを設定します。パフォーマンスを向上させるために、ThreadStaticAttribute 属性でマークされたフィールドを使用することをお勧めします。
18public void Start()
スレッドを開始します。
19public static void Sleep( int millisecondsTimeout )
スレッドを一定の時間停止させます。
20public static void SpinWait( int iterations )
iterations パラメータで定義された時間量にスレッドを待機させます。
21public 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 )

フィールドの値を読み取ります。プロセッサの数やプロセッサキャッシュの状態に関わらず、値はコンピュータのどのプロセッサからも最新の値で書き込まれます。このメソッドには様々なオーバーロードがあります。ここではいくつかの形式を示します。
22public 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 )

値をすぐにフィールドに書き込んで、その値がコンピュータのすべてのプロセッサに可见になるようにします。このメソッドには様々なオーバーロードがあります。ここではいくつかの形式を示します。
23public static bool Yield()
現在のプロセッサ上で実行準備ができた別のスレッドの実行を開始します。実行するスレッドはオペレーティングシステムが選択します。

スレッドの作成

スレッドは Thread クラスを拡張して作成されます。拡張された Thread クラスは Start() メソッドを使用して子スレッドの実行を開始します。

以下のプログラムではこの概念を示しています:

using System;
using System.Threading;
namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        public static void CallToChildThread()
        {
            Console.WriteLine("Child thread starts");
        }
        
        static void Main(string[] args)
        {
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("In Main: Creating the Child thread");
            Thread childThread = new Thread(childref);
            childThread.Start();
            Console.ReadKey();
        }
    }
}

上記のコードがコンパイルおよび実行された場合、以下のような結果が得られます:

Main 内で:子スレッドを作成しています
子スレッドが開始されました

スレッドの管理

Thread クラスは様々なスレッドの管理メソッドを提供しています。

以下の例では、 sleep() 特定の時間にスレッドを停止させるために使用されるメソッドです。

using System;
using System.Threading;
namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        public static void CallToChildThread()
        {
            Console.WriteLine("Child thread starts");
            // スレッドの停止 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("In Main: Creating the Child thread");
            Thread childThread = new Thread(childref);
            childThread.Start();
            Console.ReadKey();
        }
    }
}

上記のコードがコンパイルおよび実行された場合、以下のような結果が得られます:

Main 内で:子スレッドを作成しています
子スレッドが開始されました
Child Thread が停止しました 5 seconds
Child thread が再開します

スレッドを破棄する

Abort() メソッドはスレッドを破棄するために使用されます。

メソッドを呼び出して threadabortexception 実行中にスレッドを中止する。この例外はキャッチできません。 finally ブロック、制御が送られる finally ブロック。

以下のプログラムはこの点を説明しています:

using System;
using System.Threading;
namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        public static void CallToChildThread()
        {
            try
            {
                Console.WriteLine("Child thread starts");
                // カウントする 10
                for (int counter = 0; counter <= 10; counter++)
                {
                    Thread.Sleep(500);
                    Console.WriteLine(counter);
                }
                Console.WriteLine("Child Thread Completed");
            }
            catch (ThreadAbortException e)
            {
                Console.WriteLine("Thread Abort Exception");
            }
            finally
            {
                Console.WriteLine("Couldn't catch the Thread Exception");
            }
        }
        
        static void Main(string[] args)
        {
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("In Main: Creating the Child thread");
            Thread childThread = new Thread(childref);
            childThread.Start();
            // メインスレッドを停止させる
            Thread.Sleep(2000);
            // 現在中止子スレッド
            Console.WriteLine("In Main: Aborting the Child thread");
            childThread.Abort();
            Console.ReadKey();
        }
    }
}

上記のコードがコンパイルおよび実行された場合、以下のような結果が得られます:

Main 内で:子スレッドを作成しています
子スレッドが開始されました
0
1
2
Main 内で:子スレッドをアボートしています
Thread Abort Exception
Thread Exception をキャッチできませんでした