C# マルチスレッド
Threadはプログラムの実行パスとして定義されます。各スレッドは固有の制御フローを定義します。アプリケーションに複雑で時間のかかる操作が含まれる場合、各スレッドが特定の作業を実行するように、異なるスレッド実行パスを設定することが有益なことがよくあります。
スレッドは軽量プロセスです。スレッドの一般的な使用例は、最新のオペレーティング システムでの並列プログラミングの実装です。スレッドを使用すると、無駄な CPU サイクルが節約され、アプリケーションの効率が向上します。
これまでに作成したプログラムは、アプリケーションの実行インスタンスの単一プロセスとして実行される単一のスレッドです。ただし、このサブアプリケーションは一度に 1 つのタスクしか実行できません。複数のタスクを同時に実行するために、より小さなスレッドに分割できます。
スレッド ライフ サイクル
スレッド ライフ サイクルは、System.Threading.Thread クラスのオブジェクトが作成されたときに開始され、スレッドが終了するか実行が完了したときに終了します。
スレッド ライフ サイクルのさまざまな状態を以下に示します。
未開始状態: スレッド インスタンスが作成されたが、Start メソッドが呼び出されない状況。
準備完了状態: スレッドが実行準備ができており、CPU サイクルを待機している状態。
実行不能状態: 次の状況ではスレッドは実行できません:
Sleepメソッドが呼び出された
Waitメソッドが呼び出された
-
私を通して/ 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 クラスの一般的に使用されるプロパティとメソッド
次の表は、 スレッド クラスのいくつかの一般的に使用される 属性 :
Property | Description |
---|---|
CurrentContext | スレッドが実行されている現在のコンテキストを取得します。 |
CurrentCulture | 現在のスレッドのカルチャを取得または設定します。 |
CurrentPrinciple | スレッドの現在のプリンシパルを取得または設定します (ロールベースのセキュリティの場合)。 |
CurrentThread | 現在実行中のスレッドを取得します。 |
CurrentUICulture | 実行時にカルチャ固有のリソースを検索するためにリソース マネージャーによって使用される現在のカルチャを取得または設定します。 |
ExecutionContext | 現在のスレッドのさまざまなコンテキストに関する情報を含む ExecutionContext オブジェクトを取得します。 |
IsAlive | 現在のスレッドの実行ステータスを示す値を取得します。 |
IsBackground | スレッドがバックグラウンド スレッドであるかどうかを示す値を取得または設定します。 |
IsThreadPoolThread | スレッドがマネージド スレッド プールに属しているかどうかを示す値を取得します。 |
ManagedThreadId | 現在のマネージド スレッドの一意の識別子を取得します。 |
Name | スレッドの名前を取得または設定します。 |
Priority | スレッドのスケジューリング優先度を示す値を取得または設定します。 |
ThreadState | 現在のスレッドの状態を含む値を取得します。 |
次の表は、Thread クラスの一般的に使用されるいくつかの メソッド を示しています。
シリアル番号 | メソッド名と説明 |
---|---|
1 | public void Abort() このメソッドを呼び出しているスレッドで ThreadAbortException をスローし、このスレッドを終了するプロセスを開始します。通常、このメソッドを呼び出すとスレッドが終了します。 |
2 | public static LocalDataStoreSlot AllocateDataSlot() すべてのスレッドに名前のないデータ スロットを割り当てます。パフォーマンスを向上させるには、代わりに ThreadStaticAttribute 属性でマークされたフィールドを使用してください。 |
3 | public static LocalDataStoreSlot AllocateNamedDataSlot(
string name) すべてのスレッドに名前付きデータ スロットを割り当てます。パフォーマンスを向上させるには、代わりに ThreadStaticAttribute 属性でマークされたフィールドを使用してください。 |
4 | public static void BeginCriticalRegion() スレッドの中断や未処理の例外の影響により、アプリケーション ドメイン内の他のタスクに悪影響を及ぼす可能性があるコード領域に実行が進入しようとしていることをホストに通知します。 |
5 | public static void BeginThreadAffinity() 現在の物理オペレーティング システム スレッドの ID に依存する命令が実行されようとしていることをホスト管理コードに通知します。 |
6 | public static void EndCriticalRegion() スレッドが中止されるか未処理の例外が現在のタスクにのみ影響を及ぼすコード領域に実行が進入しようとしていることをホストに通知します。 |
7 | public static void EndThreadAffinity() 現在の物理オペレーティング システム スレッドの ID に依存する命令の実行が終了したことをホスト管理コードに通知します。 |
8 | public static void FreeNamedDataSlot(string name) プロセス内のすべてのスレッドの名前とスロットの関連付けを削除します。パフォーマンスを向上させるには、代わりに ThreadStaticAttribute 属性でマークされたフィールドを使用してください。 |
9 | public static Object GetData(
LocalDataStoreSlot スロット
) 現在のスレッドの現在のドメインにある現在のスレッドの指定されたスロットから値を取得します。パフォーマンスを向上させるには、代わりに ThreadStaticAttribute 属性でマークされたフィールドを使用してください。 |
10 | public static AppDomain GetDomain() 現在のスレッドが実行されている現在のドメインを返します。 |
11 | public static AppDomain GetDomainID() 一意のアプリケーション ドメイン識別子を返します。 |
12 | public static LocalDataStoreSlot GetNamedDataSlot(
文字列名
) 名前付きデータ スロットを見つけます。パフォーマンスを向上させるには、代わりに ThreadStaticAttribute 属性でマークされたフィールドを使用してください。 |
13 | public void Interrupt() WaitSleepJoin スレッド状態のスレッドを中断します。 |
14 | public void Join() 標準の COM および SendMessage メッセージ ポンプの処理が継続している間、いずれかのスレッドが終了するまで呼び出し元のスレッドをブロックします。このメソッドにはさまざまなオーバーロードがあります。 |
15 | public static void MemoryBarrier() メモリアクセスを次のように同期します: 現在のスレッドを実行しているプロセッサが命令を並べ替えるとき、最初に MemoryBarrier を呼び出した後はメモリアクセスを使用できません。 MemoryBarrier 呼び出しを実行します。 |
16 | public static void ResetAbort() 現在のスレッドに対して要求された中止をキャンセルします。 |
17 | public static void SetData(
LocalDataStoreSlot スロット、
オブジェクトデータ
) 現在実行中のスレッド上のこのスレッドの現在のドメインの指定されたスロットにデータを設定します。パフォーマンスを向上させるには、代わりに ThreadStaticAttribute 属性でマークされたフィールドを使用してください。 |
18 | public void Start() スレッドを開始します。 |
19 | public static void Sleep(
int ミリ秒タイムアウト
) スレッドをしばらく一時停止させてください。 |
20 | public static void SpinWait(
整数の反復
) iterations パラメーターで定義された時間だけスレッドを待機させます。 |
21 | public static byte VolatileRead(
参照バイトアドレス
) public static double VolatileRead( 参照ダブルアドレス ) public static int VolatileRead( 参照整数アドレス ) public static Object VolatileRead( refオブジェクトアドレス ) フィールド値を読み取ります。この値は、プロセッサの数やプロセッサ キャッシュの状態に関係なく、コンピュータのプロセッサによって書き込まれた最新の値です。このメソッドにはさまざまなオーバーロードがあります。ここでは一部の形式のみを示します。 |
22 | public static void VolatileWrite(
参照バイトアドレス、
バイト値
) public static void VolatileWrite( 参照ダブルアドレス、 二重値 ) public static void VolatileWrite( ref int アドレス、 int値 ) public static void VolatileWrite( ref オブジェクトアドレス、 オブジェクトの値 ) フィールドに値を即座に書き込み、その値がコンピューター内のすべてのプロセッサーに表示されるようにします。このメソッドにはさまざまなオーバーロードがあります。ここでは一部の形式のみを示します。 |
23 | public 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(); } } }
上記のコードがコンパイルされて実行されると、次の結果が生成されます:
In Main: Creating the Child thread Child thread starts
スレッドの管理
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("Child Thread Paused for {0} seconds", sleepfor / 1000); Thread.Sleep(sleepfor); Console.WriteLine("Child thread resumes"); } 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(); } } }
上記のコードをコンパイルして実行すると、次の結果が生成されます:
In Main: Creating the Child thread Child thread starts Child Thread Paused for 5 seconds Child thread resumes
スレッドを破棄します
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(); } } }
上記のコードがコンパイルされて実行されると、次の結果が生成されます:
In Main: Creating the Child thread Child thread starts 0 1 2 In Main: Aborting the Child thread Thread Abort Exception Couldn't catch the Thread Exception