.NET Framework 2.0 コア機能解説 〜 第 6 回 スレッド処理の強化 〜
青柳 臣一
Microsoft MVP for Visual Developer - Visual C#
◆スレッド処理の強化
.NET Framework 2.0ではスレッド関連の機能も強化されています。主要なものをご紹介します。
◆パラメータを渡してスレッドを実行
スレッドを実行する際になんらかのパラメータを渡したいことがあります。.NET Framework 1.0/1.1では直接スレッドに渡す方法はありませんでした。そのためスレッドメソッドをラップしたクラスを用意するなどして間接的にパラメータを渡す必要がありました。さまざまな実装方法がありますが、たとえば以下のような形になるでしょう。
C#の例 (1.0、1.1)
private class ThreadClass1x
{
private string param;
public ThreadClass1x(string param)
{
this.param = param;
}
public void Start()
{
Thread thread = new Thread(ThreadFunc);
thread.Start();
}
private void ThreadFunc()
{
Console.WriteLine("1x: {0}", this.param);
}
}
private void RunThread1x()
{
ThreadClass1x threadClass = new ThreadClass1x("parameter");
threadClass.Start();
}
Visual Basicの例 (1.0、1.1)
Private Class ThreadClass1x
Private param As String
Public Sub New(ByVal param As String)
Me.param = param
End Sub
Public Sub Start()
Dim thread As Thread = New Thread(AddressOf ThreadFunc)
thread.Start()
End Sub
Private Sub ThreadFunc()
Console.WriteLine("1x: {0}", Me.param)
End Sub
End Class
Private Sub RunThread1x()
Dim threadClass As ThreadClass1x = New ThreadClass1x("parameter")
threadClass.Start()
End Sub
.NET Framework 2.0ではスレッドに直接パラメータを渡す機能が追加されました。
System.Threading.ParameterizedThreadStartデリゲートが追加され、このデリゲートを引数に取るSystem.Threading.Threadクラスのコンストラクタも追加されています。また、Thread.Start(Object parameter)メソッドも追加されてます(以前は引数を取らないStart()メソッドのみでした)。これらを使用すると以下のサンプルコードのように直接スレッドにパラメータを渡すことができます。
C#の例 (2.0)
private void ThreadFunc20(object param)
{
Console.WriteLine("20: {0}", (string)param);
}
private void RunThread20()
{
Thread thread = new Thread(ThreadFunc20);
thread.Start("parameter");
}
Visual Basicの例 (2.0)
Private Sub ThreadFunc20(ByVal param As Object)
Console.WriteLine("20: {0}", CType(param, String))
End Sub
Private Sub RunThread20()
Dim thread As Thread = New Thread(AddressOf ThreadFunc20)
thread.Start("parameter")
End Sub
なお、ParameterizedThreadStartデリゲートのパラメータの型はObjectとなっています。ですので、配列・コレクションを使用して複数の値を渡すこともできますし、別途パラメータ渡し用のクラスを定義してそのインスタンスを渡すこともできます。
◆名前付きイベント
Mutexクラスはコンストラクタで名前を指定することにより名前付きミューテックスとすることができます。名前付きミューテックスであればスレッド間だけでなくプロセス間の同期を取ることも可能になります。
それに対して、AutoResetEventクラス、ManualResetEventクラスには名前を指定することができませんでした。そのため、これらのクラスはスレッド間の同期にのみ使用できます。
.NET Framework 2.0ではEventWaitHandleクラスが追加され、名前付きイベントを作成することが可能になりました。このクラスは事実上AutoResetEventクラス、ManualResetEventクラスを1つにまとめ、かつ、名前を付けられるようにしたものと言っていいかと思います。なお、Auto、Manualの指定もコンストラクタで行います。また、EventWaitHandleクラスもWaitHandleクラスを継承しています。ですので、MutexやAutoResetEventなど他の同期オブジェクトと混在してWaitHandle.WaitAll()メソッドなどで待ち合わせすることも可能です。
C#の例
EventWaitHandle evnt = new EventWaitHandle(false, EventResetMode.ManualReset, "global event");
evnt.WaitOne();
Visual Basicの例
Dim evnt As EventWaitHandle = New EventWaitHandle(False, EventResetMode.ManualReset, "global event")
evnt.WaitOne()
◆セマフォ
.NET Framework 2.0ではSystem.Threading. Semaphoreクラスが追加されました。セマフォを使用すると、リソースへの同時アクセスの最大数を制限することができます。
以下のサンプルコードでは、最大同時アクセス数を2に指定したセマフォを作成し、4つのスレッドに渡しています。それぞれのスレッドではSemaphore.WaitOne()メソッドでロックを獲得し、1秒間スリープした後、Semaphore.Release()メソッドでロックを解放しています。同時にロックを獲得できるのは最大2つですから、3つ目にロックを獲得しようとしたスレッドは最初のスレッドがロックを解放するまで待たされることになります。
C#の例
private class SemaphoreSampleParameter
{
public Semaphore semaphore;
public int num;
public SemaphoreSampleParameter(Semaphore semaphore, int num)
{
this.semaphore = semaphore;
this.num = num;
}
}
private static void SemaphoreSampleThreadMethod(object param)
{
SemaphoreSampleParameter p = (SemaphoreSampleParameter)param;
p.semaphore.WaitOne();
Console.WriteLine("開始:{0:d}", p.num);
Thread.Sleep(1000);
Console.WriteLine("終了:{0:d}", p.num);
p.semaphore.Release();
}
static void Main(string[] args)
{
Semaphore semaphore = new Semaphore(0, 2);
for (int i = 0; i < 4; ++i)
{
Thread thread = new Thread(SemaphoreSampleThreadMethod);
thread.Start(new SemaphoreSampleParameter(semaphore, i));
Thread.Sleep(200);
}
semaphore.Release(2);
Console.WriteLine("完了");
}
Visual Basicの例
Private Class SemaphoreSampleParameter
Public semaphore As Semaphore
Public num As Integer
Public Sub New(ByVal semaphore As Semaphore, ByVal num As Integer)
Me.semaphore = semaphore
Me.num = num
End Sub
End Class
Private Sub SemaphoreSampleThreadMethod(ByVal param As Object)
Dim p As SemaphoreSampleParameter = CType(param, SemaphoreSampleParameter)
p.semaphore.WaitOne()
Console.WriteLine("開始:{0:d}", p.num)
Thread.Sleep(1000)
Console.WriteLine("終了:{0:d}", p.num)
p.semaphore.Release()
End Sub
Sub Main()
Dim semaphore As Semaphore = New Semaphore(0, 2)
For i As Integer = 0 To 3
Dim th As Thread = New Thread(AddressOf SemaphoreSampleThreadMethod)
th.Start(New SemaphoreSampleParameter(semaphore, i))
Thread.Sleep(200)
Next
semaphore.Release(2)
Console.WriteLine("完了")
End Sub
◆スレッドのスタックサイズ
.NET Framework 2.0ではSystem.Threading.Thread.Start()メソッドでスレッドのスタックサイズを指定することができるようになりました。
◆参考資料
System.Threading.Threadクラス
http://msdn2.microsoft.com/ja-jp/library/system.threading.thread.aspx
System.Threading.EventWaitHandleクラス
http://msdn2.microsoft.com/ja-jp/library/system.threading.eventwaithandle.aspx
System.Threading.Semaphoreクラス
http://msdn2.microsoft.com/ja-jp/library/system.threading.semaphore.aspx
|