┏第20号━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃         .NETプログラミング研究         ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜メニュー ■.NET Tips ・.NETのマルチスレッドプログラミング その2 - スレッドの状態を取得する - スレッドが終了するまで待機する - スレッドを中止させる、中止をキャンセルする - スレッドを一時停止させる 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜メニュー ─────────────────────────────── ■.NET Tips ─────────────────────────────── ●.NETのマルチスレッドプログラミング その2 前回の続きです。 ★スレッドの状態を取得する スレッドの状態を取得するには、Thread.ThreadStateプロパティを使 います。スレッドは同時に複数の状態になりえるため、ThreadStateプ ロパティはThreadState列挙体の値を組み合わせた値になります。 ThreadState列挙体のすべてのメンバはヘルプ「ThreadState 列挙体」 で説明されています。 ・ThreadState 列挙体 http://www.microsoft.com/japan/msdn/library/ja/cpref/html/frlrfsystemthreadingthreadstateclasstopic.asp 下のコードは、スレッドをThread.Startで開始し、Suspendで中断し、 Resumeで再開し、Abortで終了させた時にThreadStateがどのように変 化するか調べるものです。(これらのメソッドに関しては、後ほど説 明します。) '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ Class MainClass 'エントリポイント Public Shared Sub Main() 'Threadオブジェクトを作成する Dim t1 As System.Threading.Thread = _ New System.Threading.Thread( _ New System.Threading.ThreadStart(AddressOf MyMethod1)) 'スレッドの状態を表示 Console.WriteLine("ThreadState:{0}", t1.ThreadState.ToString()) Console.WriteLine(">Start") 'スレッドを開始する t1.Start() System.Threading.Thread.Sleep(50) Console.WriteLine("ThreadState:{0}", t1.ThreadState.ToString()) Console.WriteLine(">Suspend") 'スレッドを中断 t1.Suspend() Console.WriteLine("ThreadState:{0}", t1.ThreadState.ToString()) System.Threading.Thread.Sleep(50) Console.WriteLine("ThreadState:{0}", t1.ThreadState.ToString()) Console.WriteLine(">Resume") 'スレッドの再開 t1.Resume() Console.WriteLine("ThreadState:{0}", t1.ThreadState.ToString()) System.Threading.Thread.Sleep(50) Console.WriteLine("ThreadState:{0}", t1.ThreadState.ToString()) Console.WriteLine(">Abort") 'スレッドの停止 t1.Abort() Console.WriteLine("ThreadState:{0}", t1.ThreadState.ToString()) 'スレッドが終了するまで待つ t1.Join() Console.WriteLine("ThreadState:{0}", t1.ThreadState.ToString()) Console.ReadLine() End Sub '別スレッドで実行するメソッド Private Shared Sub MyMethod1() '無限ループ While True End While End Sub End Class '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ class MainClass { //エントリポイント public static void Main() { //Threadオブジェクトを作成する System.Threading.Thread t1 = new System.Threading.Thread( new System.Threading.ThreadStart(MyMethod1)); //スレッドの状態を表示 Console.WriteLine("ThreadState:{0}", t1.ThreadState.ToString()); Console.WriteLine(">Start"); //スレッドを開始する t1.Start(); System.Threading.Thread.Sleep(50); Console.WriteLine("ThreadState:{0}", t1.ThreadState.ToString()); Console.WriteLine(">Suspend"); //スレッドを中断 t1.Suspend(); Console.WriteLine("ThreadState:{0}", t1.ThreadState.ToString()); System.Threading.Thread.Sleep(50); Console.WriteLine("ThreadState:{0}", t1.ThreadState.ToString()); Console.WriteLine(">Resume"); //スレッドの再開 t1.Resume(); Console.WriteLine("ThreadState:{0}", t1.ThreadState.ToString()); System.Threading.Thread.Sleep(50); Console.WriteLine("ThreadState:{0}", t1.ThreadState.ToString()); Console.WriteLine(">Abort"); //スレッドの停止 t1.Abort(); Console.WriteLine("ThreadState:{0}", t1.ThreadState.ToString()); //スレッドが終了するまで待つ t1.Join(); Console.WriteLine("ThreadState:{0}", t1.ThreadState.ToString()); Console.ReadLine(); } //別スレッドで実行するメソッド private static void MyMethod1() { //無限ループ for (;;); } } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 出力結果は次のようになります。 ThreadState:Unstarted >Start ThreadState:Running >Suspend ThreadState:SuspendRequested ThreadState:Suspended >Resume ThreadState:Running ThreadState:Running >Abort ThreadState:AbortRequested ThreadState:Stopped スレッド開始前はUnstarted、開始後はRunning、Suspendメソッドで一 時停止するとSuspendRequestedからSuspendedへ、Abortメソッドを呼 び出した直後はAbortRequestedで、スレッドが終了するとStoppedとな ります。 上記の例ではそうなりませんでしたが、スレッドが同時に複数の状態 になることがありますので、ThreadStateプロパティでスレッドの状態 を調べるには、AND演算子を使って、例えば、 if ((t1.ThreadState & ThreadState.Stopped) > 0) のようにします。 また、スレッドが実行中かどうか調べるだけであれば、IsAliveプロパ ティを使うこともできます。スレッドの状態がUnstarted、Stopped、 WaitSleepJoinの時はIsAliveがfalseとなります。 参考: ・スレッド状態 http://www.microsoft.com/japan/msdn/library/ja/cpguide/html/cpconthreadactivitystates.asp ・スレッドの状態 http://www.microsoft.com/japan/msdn/library/ja/vbcn7/html/vaconthreadstates.asp ★スレッドが終了するまで待機する あるスレッドが終了するまで現在のスレッドをブロックするには、 Thread.Joinメソッドを使います。別のスレッドが確実に終了したとこ ろで何らかの処理を行いたいとき(同期処理)などに便利です。(ス レッドの同期について詳しくは、別の機会に紹介する予定です。) 次にJoinメソッドを使用した簡単な例を示します。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 'エントリポイント Public Shared Sub Main() 'スレッドの作成と開始 Dim t As Thread = _ New Thread(New ThreadStart(AddressOf MyThread)) t.Start() 'スレッドtが終了するまでブロックする t.Join() Console.WriteLine("エンターキーで終了します") Console.ReadLine() End Sub Private Shared Sub MyThread() '何らかの処理があるものとする Thread.Sleep(1000) Console.WriteLine("終了しました") End Sub '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //using System.Threading; //が宣言されているものとする //エントリポイント public static void Main() { //スレッドの作成と開始 Thread t = new Thread(new ThreadStart(MyThread)); t.Start(); //スレッドtが終了するまでブロックする t.Join(); Console.WriteLine("エンターキーで終了します"); Console.ReadLine(); } private static void MyThread() { //何らかの処理があるものとする Thread.Sleep(1000); Console.WriteLine("終了しました"); } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 上記の例のようにJoinメソッドを引数なしで呼び出すと、スレッドが 終了するまで無制限にブロックされます。JoinメソッドにInt32型か TimeSpan型を指定することにより、ブロックする最大時間を指定でき ます。この時、Joinメソッドは、スレッドが終了した時はtrueを、指 定した時間が経過してもスレッドが終了しなかった時はfalseを返しま す。 Joinメソッドにより待機しているスレッドの状態は、WaitSleepJoinと なります。また、Unstarted状態のスレッドでJoinを呼び出すと、例外 がスローされます。 ★スレッドを中止させる、中止をキャンセルする 実行中のスレッドを中止させる(強制終了させる)には、そのスレッ ドのThread.Abortメソッドを呼び出します。Abortメソッドが呼び出さ れると、そのスレッドの状態はAbortRequestedとなり、その後スレッ ドが正常に終了するとStoppedになります。 Abortメソッドを呼び出してもすぐにスレッドが中止されるという保障 はありません。(セーフポイントに達した時に中止されます。セーフ ポイントに関してはヘルプ「Thread.Suspend、ガベージ コレクション、 およびセーフ ポイント」をご覧ください。)確実にスレッドが中止さ れるのを待つためには、Thread.Joinメソッドを使用します。 ・Thread.Suspend、ガベージ コレクション、およびセーフ ポイント http://www.microsoft.com/japan/msdn/library/ja/cpguide/html/cpconthreadsuspendgarbagecollectionsafepoints.asp 次の例では、別スレッドでPrintIntegerメソッドを実行し、その処理 の途中でAbortメソッドによりスレッドを中止し、Joinメソッドにより スレッドが実際に中止されるまで待機しています。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 'Imports System.Threading 'が宣言されているものとする 'エントリポイント Public Shared Sub Main() 'Threadオブジェクトを作成する Dim t As Thread = _ New Thread(New ThreadStart(AddressOf PrintInteger)) 'スレッドを開始する t.Start() 'しばらく待機する Thread.Sleep(1000) 'スレッドを中断する t.Abort() 'スレッドが終了するまで待機する t.Join() Console.ReadLine() End Sub '別スレッドで実行するメソッド Private Shared Sub PrintInteger() '無限ループ While True Dim i As Integer For i = 0 To 9 Console.Write(i) Next '到達できないコード Console.WriteLine("Thread end") End While End Sub '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //using System.Threading; //が宣言されているものとする //エントリポイント public static void Main() { //Threadオブジェクトを作成する Thread t = new Thread(new ThreadStart(PrintInteger)); //スレッドを開始する t.Start(); //しばらく待機する Thread.Sleep(1000); //スレッドを中断する t.Abort(); //スレッドが終了するまで待機する t.Join(); Console.ReadLine(); } //別スレッドで実行するメソッド private static void PrintInteger() { //無限ループ for (;;) for (int i = 0; i < 10; i++) Console.Write(i); //到達できないコード Console.WriteLine("Thread end"); } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ Abortメソッドを呼び出すと、スレッドでThreadAbortExceptionがスロー されます。ThreadAbortExceptionをキャッチし、Thread.ResetAbortメ ソッドを呼び出すことにより、Abortメソッドにより行われた中止の要 求をキャンセルすることが出来ます。なお、ResetAbortメソッドは ThreadAbortExceptionが発生したスレッドからのみ呼び出すことがで きます。 次の例では上記のコードに追加して、ThreadAbortExceptionをキャッ チし、ResetAbortメソッドにより、中止をキャンセルしています。そ の結果、その後の Console.WriteLine("Thread end"); が実行されるようになることに注目してください。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 'Imports System.Threading 'が宣言されているものとする 'エントリポイント Public Shared Sub Main() 'Threadオブジェクトを作成する Dim t As Thread = _ New Thread(New ThreadStart(AddressOf PrintInteger)) 'スレッドを開始する t.Start() 'しばらく待機する Thread.Sleep(1000) 'スレッドを中断する t.Abort() 'スレッドが終了するまで待機する t.Join() Console.ReadLine() End Sub '別スレッドで実行するメソッド Private Shared Sub PrintInteger() Try '無限ループ While True Dim i As Integer For i = 0 To 9 Console.Write(i) Next End While Catch ex As ThreadAbortException 'Abortをキャンセルする Thread.ResetAbort() End Try 'Thread.ResetAbortにより、以下が実行されるようになる Console.WriteLine("Thread end") End Sub '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //using System.Threading; //が宣言されているものとする //エントリポイント public static void Main() { //Threadオブジェクトを作成する Thread t = new Thread(new ThreadStart(PrintInteger)); //スレッドを開始する t.Start(); //しばらく待機する Thread.Sleep(1000); //スレッドを中断する t.Abort(); //スレッドが終了するまで待機する t.Join(); Console.ReadLine(); } //別スレッドで実行するメソッド private static void PrintInteger() { try { //無限ループ for (;;) for (int i = 0; i < 10; i++) Console.Write(i); } catch (ThreadAbortException) { //Abortをキャンセルする Thread.ResetAbort(); } //Thread.ResetAbortにより、以下が実行されるようになる Console.WriteLine("Thread end"); } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ ただしヘルプ(「マネージ スレッド処理の実施」)には、 「他のスレッドを終了させるために Thread.Abort を使用することは 避けてください。他のスレッドの Abort を呼び出すことは、そのスレ ッドの処理がどこまで到達しているかを把握せずに例外をスローする のと同じことになります。」 と書かれていますので、注意が必要です。(別スレッドに対して Abortを呼び出したときの問題点について詳しくは、ヘルプの「 Thread.Abort メソッド」をご覧ください。) ・Thread.Abort メソッド http://www.microsoft.com/japan/msdn/library/ja/cpref/html/frlrfsystemthreadingthreadclassaborttopic.asp ★スレッドを一時停止させる スレッドを一時的に中断するにはThread.Suspendメソッドを、中断さ れたスレッドを再開するにはThread.Resumeメソッドを呼び出します。 Thread.Abortと同様にThread.Suspendメソッドを呼び出してすぐにス レッドの実行が中断されるわけではなく、セーフポイントに到達する までは中断されません。 Suspendメソッドが何回呼び出されていたとしても、Resumeメソッドを 一回呼び出すだけで再開されます。また、起動していないスレッドや、 停止しているスレッドに対してSuspendメソッドを呼び出すと、例外( ThreadStateException)がスローされます。 次の例では、起動したスレッド(t)をSuspendメソッドで中断させた後、 しばらくしてからResumeメソッドで再開しています。 '[VB.NET]・・・・・・・・・・・・・・・・・・・・・・・・・・ 'エントリポイント Public Shared Sub Main() 'PrintIntegerメソッドを実行するための 'Threadオブジェクトを作成する Dim t As System.Threading.Thread = _ New System.Threading.Thread( _ New System.Threading.ThreadStart(AddressOf PrintInteger)) 'スレッドを開始する t.Start() 'しばらく待機する System.Threading.Thread.Sleep(100) Console.Write("<< Suspend >>") 'スレッドを中断する t.Suspend() '再びしばらく待機する System.Threading.Thread.Sleep(1000) '中断したスレッドを再開する t.Resume() Console.Write("<< Resumed >>") 'みたびしばらく待機する System.Threading.Thread.Sleep(100) Console.Write("<< Abort >>") 'スレッドを終了する t.Abort() Console.ReadLine() End Sub '別スレッドで実行するメソッド Private Shared Sub PrintInteger() '無限ループ While True Dim i As Integer For i = 0 To 9 Console.Write(i) Next End While End Sub '・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //[C#]・・・・・・・・・・・・・・・・・・・・・・・・・・・・ //エントリポイント public static void Main() { //PrintIntegerメソッドを実行するための //Threadオブジェクトを作成する System.Threading.Thread t = new System.Threading.Thread( new System.Threading.ThreadStart(PrintInteger)); //スレッドを開始する t.Start(); //しばらく待機する System.Threading.Thread.Sleep(100); Console.Write("<< Suspend >>"); //スレッドを中断する t.Suspend(); //再びしばらく待機する System.Threading.Thread.Sleep(1000); //中断したスレッドを再開する t.Resume(); Console.Write("<< Resumed >>"); //みたびしばらく待機する System.Threading.Thread.Sleep(100); Console.Write("<< Abort >>"); //スレッドを終了する t.Abort(); Console.ReadLine(); } //別スレッドで実行するメソッド private static void PrintInteger() { //無限ループ for (;;) for (int i = 0; i < 10; i++) Console.Write(i); } //・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ (補足: 上記のコードでは、 t.Suspend(); と t.Resume(); の間にConsole.Writeを入れると、そこでフリーズしてしまいました。) スレッドを一時停止させる方法として、Thread.Sleepメソッドを使う こともできます。SleepメソッドはSuspendメソッドと違い、呼び出す とスレッドはすぐに停止しますが、停止するスレッド以外のスレッド から呼び出すことはできません。 なお、この方法はスレッドを同期させる方法としては不適切です。ス レッドの同期につきましては、別の機会に紹介する予定です。 参考: ・スレッドの一時中断と再開 http://www.microsoft.com/japan/msdn/library/ja/cpguide/html/cpconworkingwiththreads.asp =============================== ■このマガジンの購読、購読中止、バックナンバー、説明に関しては  次のページをご覧ください。  http://www.mag2.com/m/0000104516.htm ■発行人・編集人:どぼん!  http://dobon.net  dobon@bigfoot.com ■ご質問等はメールではなく、掲示板へお願いいたします。  http://dobon.net/vb/bbs.html ■上記メールアドレスへのメールは確実に読まれる保障はありません  (スパム、ウィルス対策です)。メールは下記URLのフォームメール  から送信してください。  http://dobon.net/mail.html Copyright (c) 2003 DOBON! All rights reserved. ===============================