ミューテックスとは
ミューテックス(Mutex = MUTual EXclusion)とは排他制御を行う上で用いられる概念のひとつ。 ミューテックスは、ミューテックス(=排他区間)へ入ることを要求するP操作、ミューテックスから出ることを通知するV操作から構成される。 ミューテックスは同時に1つの処理しか排他区間に入ることを許可しないため、カウンタの初期値が1であるセマフォと同義である。 セマフォについての詳細はSystem.Threading.Semaphoreにて解説している。
.NET FrameworkにおけるMutex
基本的にはカウンタの初期値が1に固定されたSystem.Threading.Semaphoreと同じ。 名前付きでMutexインスタンスを生成すると、オペレーティングシステム全体から参照できる(プロセス間で共有できる)システムミューテックスとなり、名前なしでMutexインスタンスを生成すると、プロセス内でのみ参照できるローカルミューテックスとなる。
ミューテックスに入るにはWaitOne()、ミューテックスから出るにはReleaseMutex()を呼ぶ。 ただし、Semaphore.Release()とは異なり、ReleaseMutex()はWaitOne()したスレッドからしか呼び出すことが許可されない。 つまり、ミューテックスに入ったスレッド以外はミューテックスから出ることが出来ない(他のスレッドからは操作できない)。 ミューテックスに入っている処理(スレッド)がない場合、ミューテックスは「シグナル状態」(signaled)となる。 ミューテックスがシグナル状態になると、WaitOne()でブロックされている処理はミューテックスに入ることができるようになる。
システムミューテックスの場合ミューテックスには「所有権」という概念があり、システムミューテックスを生成したスレッドは「所有権」を持つことができる。 ミューテックスの所有権を持ったスレッドが終了した場合、ミューテックスは「放棄された状態」(abandoned)となる。 このとき、ミューテックスはシグナル状態となるが、他のスレッドがミューテックスに入ろうと待機(WaitOne)していた場合、そのスレッドではAbandonedMutexExceptionが発生する。
「所有権」の有無はインスタンスの生成時に指定できる(初期所有権)。 初期所有権を付与しなかった場合は、システムミューテックスを生成したスレッドが終了した後に他のスレッドが放棄されたミューテックスに入ろうとしても、AbandonedMutexExceptionは発生しない。
主なコンストラクタ
- Mutex()
- 名前なしミューテックスのインスタンスを生成する。
- Mutex(bool initiallyOwned, string name)
- 初期所有権の有無、ミューテックスの名前を指定してインスタンスを生成する。
- Mutex(bool initiallyOwned, string name, out bool createdNew)
- 初期所有権の有無、ミューテックスの名前を指定してインスタンスを生成する。
新しく名前付きミューテックスを作成した場合はcreatedNewにtrue、すでに同名の名前付きミューテックスが存在している場合はfalseがセットされる。
主なメソッド
- static Mutex OpenExisting(string name)
- すでに作成されている名前付きミューテックスを取得する。 取得できない場合、存在しない場合は、WaitHandleCannotBeOpenedExceptionがスローされる。
- bool WaitOne()
- ミューテックスに入る(P操作)。 すでに他のスレッドがミューテックスに入っている場合は、ReleaseMutex()が呼ばれる(V操作が行われる)まで処理がブロックされる。
- void ReleaseMutex()
- ミューテックスから出る(V操作)。
- void Close()
- インスタンスが保持しているリソースを解放する。
主な呼び出し順序
ローカルミューテックスの場合。
- new Mutex()
- WaitOne()
- Release()
- Close()
システムミューテックスの場合。
- 新しく生成する場合はnew Mutex()、既に生成されたものを参照する場合はMutex.OpenExisting()
- WaitOne()
- Release()
- Close()
使用例
システムミューテックスを生成し、排他区間に対して複数のプロセスから同時にアクセスを試みる。
using System;
using System.Threading;
using System.Diagnostics;
class Sample {
static void Main()
{
var processId = Process.GetCurrentProcess().Id;
Console.WriteLine("{0} started", processId);
// プロセス間で共有する名前付きミューテックスを作成(初期所有権は付与しない)
const string mutexName = "named-mutex-sample";
bool createdNew;
using (var mutex = new Mutex(false, mutexName, out createdNew)) {
// このプロセスで名前付きミューテックスを生成した
if (createdNew)
Console.WriteLine("{0} mutex created", processId);
Console.WriteLine("{0} waiting", processId);
// ミューテックスに入る (入れるまで処理をブロックする)
mutex.WaitOne();
Console.WriteLine("{0} -> enter", processId);
// 完了するまで時間のかかる処理を想定
Thread.Sleep((new Random(processId)).Next(100, 500));
// ミューテックスを出る
mutex.ReleaseMutex();
Console.WriteLine("{0} <- exited", processId);
}
}
}
Imports System
Imports System.Threading
Imports System.Diagnostics
Class Sample
Shared Sub Main()
Dim processId As Integer = Process.GetCurrentProcess().Id
Console.WriteLine("{0} started", processId)
' プロセス間で共有する名前付きミューテックスを作成(初期所有権は付与しない)
Const mutexName As String = "named-mutex-sample"
Dim createdNew As Boolean
Using mutex As New Mutex(False, mutexName, createdNew)
' このプロセスで名前付きミューテックスを生成した
If createdNew Then Console.WriteLine("{0} mutex created", processId)
Console.WriteLine("{0} waiting", processId)
' ミューテックスに入る (入れるまで処理をブロックする)
mutex.WaitOne()
Console.WriteLine("{0} -> enter", processId)
' 完了するまで時間のかかる処理を想定
Thread.Sleep((New Random(processId)).Next(100, 500))
' ミューテックスを出る
mutex.ReleaseMutex()
Console.WriteLine("{0} <- exited", processId)
End Using
End Sub
End Class
上記プログラムを5プロセス同時に実行した場合の動作例。
>csc test.cs >test.exe >&2 | test.exe >&2 | test.exe >&2 | test.exe >&2 | test.exe 2328 started 2328 mutex created 2328 waiting 2328 -> enter 3952 started 3720 started 3952 waiting 3720 waiting 4624 started 4624 waiting 4940 started 4940 waiting 2328 <- exited 4940 -> enter 3720 -> enter 4940 <- exited 4624 -> enter 3720 <- exited 4624 <- exited 3952 -> enter 3952 <- exited
Monoでは、環境変数MONO_ENABLE_SHMをセットして共有メモリの使用を有効にしないと名前付きミューテックスを使用できない。
$ mcs test.cs && for i in `seq 1 5` ; do MONO_ENABLE_SHM=1 mono test.exe & done 22269 started 22269 mutex created 22269 waiting 22269 -> enter 22267 started 22267 waiting 22265 started 22265 waiting 22268 waiting 22266 started 22266 waiting 22269 <- exited 22267 -> enter 22267 <- exited 22268 -> enter 22268 <- exited 22265 -> enter 22265 <- exited 22266 -> enter 22266 <- exited