マルチスレッド・プログラミングの基礎についての話題は以上であるが、よりマルチスレッドを活用するためのいくつかの話題を簡単に紹介しておこう。
■Mutex(ミューテックス)による排他制御
Mutexクラス(System.Threading名前空間)はlockステートメントと同じ排他制御を行うための手段である(「mutex」とは「相互排除」の意味)。Mutexオブジェクトを作成し、WaitOneメソッドでロックを取得し、ReleaseMutexメソッドでロックを解放することで排他制御を行う。
lockステートメントとの違いは、Mutexクラスではスレッド間だけではなくプロセス間での排他制御が行えることである。これによって、アプリケーションをまたがった排他制御を簡単に実現することができ、アプリケーションの多重起動のチェックなどによく使用される。次のList4は単純なMutexクラスによる排他制御のサンプルである。
using System;
using System.Threading;
public class List4
{
// Mutexオブジェクトの作成
public static Mutex mutex = new Mutex();
public static void Main()
{
for (int i = 0; i < 20; i++)
{
(new Thread(new ThreadStart(Transaction))).Start();
}
}
private static void Transaction()
{
try
{
// Mutexロック取得
mutex.WaitOne();
Console.WriteLine("トランザクション開始");
Thread.Sleep(100);
Console.WriteLine("トランザクション終了");
}
catch
{
}
finally
{
// Mutexロック解放
mutex.ReleaseMutex();
}
}
}
Imports System
Imports System.Threading
Public Class List4
' Mutexオブジェクトの作成
Public Shared mutex As New Mutex()
Public Shared Sub Main()
Dim i As Integer
For i = 0 To 19
Dim thread As Thread = New Thread(New ThreadStart(AddressOf Transaction))
thread.Start()
Next i
End Sub 'Main
Private Shared Sub Transaction()
Try
' Mutexロック取得
mutex.WaitOne()
Console.WriteLine("トランザクション開始")
Thread.Sleep(100)
Console.WriteLine("トランザクション終了")
Catch
Finally
' Mutexロック解放
mutex.ReleaseMutex()
End Try
End Sub 'Transaction
End Class 'List4
Mutexクラスを利用したサンプル・プログラムは「.NET TIPS:Windowsアプリケーションの多重起動を禁止するには?」でも紹介されているので参照していただきたい。
■Monitorクラス以外の同期制御の方法
今回では同期制御の手段としてMonitorクラスについて解説したが、これ以外にもイベントを用いる方法(AutoResetEventクラスやManualResetEventクラスを利用する方法)もSystem.Threading名前空間には用意されている。
■スレッドごとのデータ領域となるデータスロット
ThreadクラスにはAllocateDataSlot/AllocateNamedDataSlotという静的メソッドが用意されている。これらを利用することで、「データスロット」と呼ばれるスレッドごとにデータを格納しておく領域を確保することができる。スレッドごとに個別に保持したいデータなどがある場合に利用する価値があるだろう。
確保したデータスロットに対してはSetDataメソッドでデータの設定を、GetDataメソッドでデータの取得を行う。不要となったデータスロットはFreeNamedDataSlotメソッドで解放しておく必要がある。
■現在のスレッドを取得する
Threadクラスの静的メソッドであるCurrentThreadメソッドを使用することで、現在のコードがどのスレッドで実行されているのかを取得することができる。
Thread thread = Thread.CurrentThread;
■スレッドセーフなコレクション・クラス
ArrayListクラスやHashtableクラスなど.NET Frameworkのコレクション・オブジェクト(System.Collections名前空間のクラス)を複数のスレッドからアクセスするときには、「SyncRoot」というプロパティを用いてコレクションをスレッドセーフにすることができる。このプロパティは、データの先頭から順番にコレクションの中身の一覧を表示するときに、途中で要素の挿入などがないようにするためになどに使用される。
具体的なサンプル・コードなどは「.NET TIPS:スレッドセーフなコレクション・オブジェクトを作成するには?」を参照していただきたい。
■lockステートメントでロック対象とするオブジェクト
いくつかのサンプル・コードでは、よく「this」(VB.NETの場合には「Me」)や、typeof演算子(VB.NETではGetType演算子)を用いたロックの取得が頻繁に使用されているが、実際にはそういったコードはロック排他制御によるパフォーマンスの低下を引き起こしやすいプログラムになる。なぜなら、クラスのインスタンスやクラスのタイプ(Typeオブジェクト)のロックは、そのクラスのコード以外の場所でもできてしまうからである(無関係な処理の間で排他制御を行ってしまうことになる)。
もしクラスの外部で思いもかけずに長時間ロックを保持してしまうような処理があれば、その影響はクラスの内部にまで及んでしまう。このような状況を避けるために、スレッドセーフなクラスでロックを利用するときには、ロック専用のオブジェクトを作成し排他制御を行うとよい。
public class Resource
{
public static void Method()
{
lock (typeof(Resource))
{
// 何かの処理
}
}
}
public class Resource
{
private readonly object lockObject = new object();
public static void Method()
{
lock (lockObject)
{
// 何かの処理
}
}
}
この記述方法は、本連載のサンプル・プログラムでもすでに何度か利用している。
以上、全4回にわたり解説してきたが、最後にマルチスレッド・プログラミングを行うに当たっての留意点をまとめておこう。
マルチスレッド・プログラミングの要求はこれからますます増していくと考えられる。特にCPUがデュアルコア化してくると、普通のPCが複数のCPUを持つことに等しく、マルチスレッド化によってパフォーマンスの向上が期待できる場面はますます多くなってくるだろう。
しかし、マルチスレッド・プログラミングにはシングルスレッド・プログラミングにはなかった注意すべき点が多い。本連載がマルチスレッド・プログラムを開発する指針になれば幸いである。
Copyright© Digital Advantage Corp. All Rights Reserved.