これは、クリティカルセクションと似ています。 クリティカルセクションはそのプロセスでしか利用できませんが、ミューテックスは複数のプロセスで利用できます。
複数のプロセスで利用する場合は、ミューテックスの名前を付ける必要があります。
一つのプロセスのみで利用する場合は、名前は付けなくてもかまいません。 ミューテックスは、ただ一つのスレッドのみが所有できます。 所有権を放棄するまで他のスレッドは、これを所有できません。
ミューテックスを作成するにはCreateMutex関数を使います。
HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, // セキュリティ記述子 BOOL bInitialOwner, // 最初の所有者 LPCTSTR lpName // オブジェクトの名前 );lpMutexAttributesには、セキュリティ属性を指定します。NULLだとデフォルトの セキュリティとなります。
bInitialOwnerには、この関数を呼び出したスレッドが最初の所有者になるか どうかを指定します。TRUEなら所有者となり、FALSEならなりません。
lpNameには、名前を指定します。
成功するとミューテックスのハンドルが返ります。
同じ名前のミューテックスがすでに存在しても、失敗にはなりません。
関数が失敗するとNULLが返されます。
ミューテックスが不要になったら必ずCloseHandleします。
スレッドがミューテックスを所有するには、ミューテックスのハンドルを指定して 待機関数を呼び出します。
所有権を放棄するには、ReleaseMutex関数を呼び出します。
BOOL ReleaseMutex( HANDLE hMutex // ミューテックスオブジェクトのハンドル );成功すると0以外の数値が、失敗すると0が返されます。
では、サンプルを見てみましょう。
2つの子スレッドが、グローバル変数iを見て、これを表示します。 そして、iの数字を1つ増やします。
これをbThEndがTRUEになるまで延々と繰り返します。
ユーザーが何かキーを押すと、bThEndがTRUEになり、各スレッドに 終了を告げます。
/* mult07.c */ #define MUTEXNAME "MYMUTEX" #include <stdio.h> #include <conio.h> #include <windows.h> #include <process.h> unsigned __stdcall mythread0(LPVOID); unsigned __stdcall mythread1(LPVOID); HANDLE hEvent[2]; BOOL bThEnd = FALSE; int i; int main() { int i; HANDLE hTh[2]; DWORD thID[2]; HANDLE hMutex; hMutex = CreateMutex(NULL, FALSE, MUTEXNAME); if (hMutex == NULL) { printf("ミューテックス作成失敗\n"); return -1; } hEvent[0] = CreateEvent(NULL, TRUE, FALSE, "CH0"); hEvent[1] = CreateEvent(NULL, TRUE, FALSE, "CH1"); hTh[0] = (HANDLE)_beginthreadex( NULL, 0, mythread0, &hMutex, CREATE_SUSPENDED, &thID[0] ); if (hTh[0] == NULL) { printf("スレッド0作成失敗\n"); return -1; } hTh[1] = (HANDLE)_beginthreadex( NULL, 0, mythread1, &hMutex, CREATE_SUSPENDED, &thID[1] ); if (hTh[1] == NULL) { printf("スレッド1作成失敗\n"); return -1; } //各スレッド実行開始 for (i = 0; i < 2; i++) ResumeThread(hTh[i]); _getch(); bThEnd = TRUE; WaitForMultipleObjects(2, hEvent, TRUE, INFINITE); for (i = 0; i < 2; i++) { if (CloseHandle(hTh[i])) { printf("hTh[%d]のクローズに成功\n", i); } else { printf("hTh[%d]のクローズ失敗\n", i); } } if (CloseHandle(hMutex)) { printf("ミューテックスハンドルのクローズに成功\n"); } else { printf("ミューテックスハンドルのクローズに失敗\n"); } printf("親を終了します\n"); return 0; }main関数では、bThEndをTRUEにした後、WaitForMultipleObjects関数で 2つのイベントがシグナル状態になるのを待ちます。
各スレッドは、ループを抜けた時に、イベントをシグナル状態にします。
2つのスレッドがループを抜けて終了するのを待っているわけです。
unsigned __stdcall mythread0(LPVOID lpx) { HANDLE hM; hM = *(HANDLE *)lpx; while (!bThEnd) { WaitForSingleObject(hM, INFINITE); printf("スレッド0が%dを表示\n", i++); ReleaseMutex(hM); } WaitForSingleObject(hM, INFINITE); printf("スレッド0終了\n"); ReleaseMutex(hM); SetEvent(hEvent[0]); \ return 0; }第一引数で、ミューテックスハンドルをもらいます。
bThEndがTRUEになるまで、以下のことを繰り返します。
WaitForSingleObject関数でミューテックスの所有権を取得します。
グローバル変数のiを表示して、1増やします。
ミューテックスの所有権を放棄します。
bThEndがTRUEになってループを抜けた後、 また、WaitForSingleObject関数で、ミューテックスの所有権を取得します。
そして、スレッドが終了することを表示します。
表示が終わったらミューテックスの所有権を放棄します。
ループを抜けた後、ミューテックスの所有は必要ないように思われるかもしれませんが これをしないとおかしなことが起ります。
「スレッド0終了」と表示している最中に、もう一つのスレッドが表示を開始したり あるいは「スレッド0終了」が2回表示されたりします。
さて、終了の表示が終わったら、イベントをシグナル状態にします。
unsigned __stdcall mythread1(LPVOID lpx) { HANDLE hM; hM = *(HANDLE *)lpx; while (!bThEnd) { WaitForSingleObject(hM, INFINITE); printf("スレッド1が%dを表示\n", i++); ReleaseMutex(hM); } WaitForSingleObject(hM, INFINITE); printf("スレッド1終了\n"); ReleaseMutex(hM); SetEvent(hEvent[1]); return 0; }mythread0関数とほぼ同じ内容ですが、最後にhEvent[1]をシグナル状態にするところが 違います。
では、実行結果を見てみましょう。
Update Nov/30/2004 By Y.Kumei