今回はミューテックスについてやります。これは、クリティカルセクションと似ています。 クリティカルセクションはそのプロセスでしか利用できませんが、ミューテックスは複数のプロセスで利用できます。
複数のプロセスで利用する場合は、ミューテックスの名前を付ける必要があります。
一つのプロセスのみで利用する場合は、名前は付けなくてもかまいません。 ミューテックスは、ただ一つのスレッドのみが所有できます。 所有権を放棄するまで他のスレッドは、これを所有できません。
ミューテックスを作成するには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