前述したように、.NETにおいて、マルチスレッドを実現するための最も基本となるのがThreadクラスを使用した方法である。
List1-1は、Threadクラスを用いて「ThreadMethod」という名前のメソッドを別スレッドで動作させる簡単なプログラムである。.NETにおけるマルチスレッド・プログラミングが非常に簡単であることが分かっていただけるだろう。
using System;
using System.Threading;
public class List1_1
{
public static void Main()
{
Thread threadA = new Thread(
new ThreadStart(ThreadMethod)); // (1)
threadA.Start(); // (2)
for(int i = 0; i < 100; i++)
{
Thread.Sleep(5);
Console.Write(" B ");
}
}
// 別スレッドで動作させるメソッド
private static void ThreadMethod()
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(5);
Console.Write(" A ");
}
}
}
Imports System
Imports System.Threading
Public Class List1_1
Public Shared Sub Main()
Dim threadA As New Thread( _
New ThreadStart(AddressOf ThreadMethod)) ' (1)
threadA.Start() ' (2)
For i As Integer = 0 To 99
Thread.Sleep(5)
Console.Write(" B ")
Next i
End Sub
' 別スレッドで動作させるメソッド
Private Shared Sub ThreadMethod()
For i As Integer = 0 To 99
Thread.Sleep(5)
Console.Write(" A ")
Next i
End Sub
End Class
Threadクラスのインスタンスを作成するには、コンストラクタのパラメータにThreadStartクラスのオブジェクトを指定する。ThreadStartクラスのコンストラクタには、別スレッドで動作させたいメソッド名を指定すればよい((1))。
Thread threadA = new Thread(new ThreadStart(ThreadMethod)); // (1)
(以下、解説はC#のプログラムを使用)
そして、作成したThreadオブジェクトのStartメソッドを呼び出すことで、別スレッドによる処理(ThreadMethodメソッドの実行)が開始される((2))。
threadA.Start(); // (2)
ただし、Threadクラスを使用して別スレッドで動作させることができるメソッドは、戻り値がvoidで、パラメータを取らないメソッドのみである。戻り値やパラメータを利用したいときは、後述のデリゲートによるスレッド処理を使用する。
さて、このコードの実行結果を見ると、AとBがランダムに100個ずつ表示され、2つのスレッド(MainメソッドとThreadMethodメソッド)が並行して動作していることが分かるだろう。
B A B A B A B A B A B A A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A B A A B A B A B A B A B A B A B A B A B A B A B A B B B
Threadクラスを使用したメソッドの呼び出しは、直接的にはパラメータが使えないわけだが、これでは実際にプログラミングを行ううえで何かと不便な場合が多い。
そこで、スレッド処理を行うクラスを1つ作成し、そのクラスのフィールドを通じて、メソッドに何らかのデータを渡す方法がよく用いられる。以下にそのサンプル・プログラムを示す(List1-2)。
using System;
using System.Threading;
public class List1_2
{
public static void Main()
{
ThreadedTextPrinter printer = new ThreadedTextPrinter(); // (1)
printer.Print("A"); // (2)
for (int i = 0; i < 100; i++)
{
Thread.Sleep(5);
Console.Write(" B ");
}
}
}
public class ThreadedTextPrinter // (3)
{
private string text;
public void Print(string s) // (4)
{
text = s; // (5)
Thread thread = new Thread(new ThreadStart(ThreadMethod));
thread.Start();
}
// 別スレッドで動作させるメソッド
private void ThreadMethod() // (6)
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(5);
Console.Write(" {0} ", text); // (7)
}
}
}
Imports System
Imports System.Threading
Public Class List1_2
Public Shared Sub Main()
Dim printer As New ThreadedTextPrinter() ' (1)
printer.Print("A") ' (2)
For i As Integer = 0 To 99
Thread.Sleep(5)
Console.Write(" B ")
Next i
End Sub
End Class
Public Class ThreadedTextPrinter ' (3)
Private text As String
Public Sub Print(s As String) ' (4)
text = s ' (5)
Dim thread As New Thread( _
New ThreadStart(AddressOf ThreadMethod))
thread.Start()
End Sub
' 別スレッドで動作させるメソッド
Private Sub ThreadMethod() ' (6)
For i As Integer = 0 To 99
Thread.Sleep(5)
Console.Write(" {0} ", text) ' (7)
Next i
End Sub
End Class
ここでは、ThreadedTextPrinterというクラスを新たに作成した((3))。これが別スレッドにより、ある文字を出力するという機能を提供するクラスになる。その唯一のpublicメソッドとしてPrintメソッド((4))を用意し、出力する文字列をパラメータとして渡すことができるようになっている。このPrintメソッドは通常のインスタンス・メソッドであり、何ら特別な仕組みを意識することなく呼び出すことができる((1)、(2))。
Printメソッドでは、まずパラメータとして受け取った文字列をThreadedTextPrinterクラスのフィールド「text」に代入している((5))。インスタンスのフィールドを介することにより、Threadクラスによるメソッド呼び出しにデータを渡すことができるようになるのである。
別スレッドで実行されるThreadMethodメソッド((6))の内部では、先ほどPrintメソッドのパラメータの値をコピーしておいたtextを読み込み((7))、それを出力している。このようにThreadTextPrinterクラスでスレッドの呼び出しをカプセル化することによって、マルチスレッドによる処理を外部に意識させないコードも記述可能だ。
Threadクラスを利用して実行したスレッドは、デフォルトで「フォアグラウンド・スレッド」として実行される。フォアグラウンド・スレッドとはそのスレッドが動作している限り、アプリケーションが終了しないスレッドのことである。そして、この逆が「バックグラウンド・スレッド」になる。
バックグラウンド・スレッドが動作中にメインのスレッドが終了すれば、バックグラウンド・スレッドは強制的に終了し、アプリケーションも終了する。Threadクラスには、バックグラウンド・スレッドであるかどうかを指定/取得するIsBackgroundプロパティが用意されている。ちなみにスレッドプールやデリゲートによるスレッドはバックグラウンド・スレッドとして実行される。
thread.IsBackground = true; // バックグラウンド・スレッドとする
また、スレッドの優先度を変更するには、Priorityプロパティを利用する。優先度の高いスレッドは、ほかのスレッドより優先してスケジューリングされる(これにより結果的には、より多くの実行時間が与えられる)。
PriorityプロパティにはThreadPriority列挙体の値を指定するが、デフォルトはThreadPriority.Normalである。以下の例は、最優先の優先度をスレッドに与えている。
thread.Priority = ThreadPriority.Highest; //優先度を「最優先」にする
次に、Threadクラスに用意されているメソッドを簡単に紹介しておく。Suspendメソッドは実行中のスレッドを一時停止するときに使用する。また、Resumeメソッドは一時停止中のスレッドを再び実行状態に戻すためのメソッドである。
thread.Suspend(); // スレッドの一時停止
thread.Resume(); // スレッドの再開
Joinメソッドは、現在別スレッドとして動作している処理の終了を待つためのメソッドである。処理が終了するまで、呼び出し側のスレッドもブロックされるので注意が必要である。
thread.Join(); // スレッドの処理が終了するまで待つ
スレッドを強制的に終了させたいという場合にはAbortメソッドを利用する。
thread.Abort(); // スレッドの強制終了
最後にSleepメソッドを紹介する。Sleepメソッドはパラメータにミリ秒(1秒=1000ミリ秒)を指定することができ、指定した時間だけ、そのスレッドの処理をストップさせることができる。このSleepメソッドはThreadクラスのインスタンス・メソッドではなく、静的メソッドであるためいつでも利用できる。
Thread.Sleep(1000); // 1秒間待つ
Copyright© Digital Advantage Corp. All Rights Reserved.