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