今回は、セマフォについてやります。
セマフォはちょっと説明が難しいです。
セマフォは、現在値を持ちます。これが0以外であれば、Waitナンタラ関数 は直ちに帰ります。この時セマフォの値は1減ります。
セマフォが0になった時、Waitナンタラ関数は0より大になるまで待機します。
ロックを解放するにはReleaseSemaphore関数を使います。これにより、セマフォの値 は1増えます。
セマフォを作成するにはCreateSemaphore関数を使います。
HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // セキュリティ記述子 LONG lInitialCount, // 初期カウント LONG lMaximumCount, // 最大カウント LPCTSTR lpName // オブジェクト名 );lpSemaphoreAttributesには、セキュリティ記述子を指定します。 NULLを指定するとデフォルトのセキュリティとなります。
lInitialCountには、初期のセマフォのカウントを指定します。
lMaximumCountには、カウントの最大値を指定します。
lpNameには、セマフォの名前を指定します。
戻り値はセマフォのハンドルです。関数が失敗した時はNULLが返ります。
プロセスが終了する時、プロセスが所有していたハンドルを自動的に閉じます。 明示的に閉じる時はCloseHandle関数を使います。
セマフォのカウントを増やすにはReleaseSemaphore関数を使います。
BOOL ReleaseSemaphore( HANDLE hSemaphore, // セマフォのハンドル LONG lReleaseCount, // カウントを増やすべき数 LPLONG lpPreviousCount // それまでのカウント );hSemaphoreには、セマフォのハンドルを指定します。
lReleaseCountには、カウントをいくつ増やすかを指定します。0より大きい数を指定します。
lpPreviousCountには、それまでのカウントを取得する変数のポインタを指定します。 不要であれば、NULLを指定できます。
関数が成功すると0以外が、失敗すると0が返ります。
さて、セマフォの最大カウントを1にすると、ミューテックスと同じ動作になります。 これをバイナリセマフォと呼ぶことがあります。
サンプルでは、最大カウントを1にして前章と同じサンプルを書き直してみます。
/* mult08.c */
#define SEMAPHORENAME "MYSEMAPHORE"
#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 hSemaphore;
    hSemaphore = CreateSemaphore(NULL, 1, 1, SEMAPHORENAME);
    if (hSemaphore == 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,
        &hSemaphore,
        CREATE_SUSPENDED,
        &thID[0]
    );
    if (hTh[0] == NULL) {
        printf("スレッド0作成失敗\n");
        return -1;
    }
    hTh[1] = (HANDLE)_beginthreadex(
        NULL,
        0,
        mythread1,
        &hSemaphore,
        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(hSemaphore)) {
        printf("セマフォハンドルのクローズに成功\n");
    } else {
        printf("セマフォハンドルのクローズに失敗\n");
    }
    printf("親を終了します\n");
    return 0;
}
unsigned __stdcall mythread0(LPVOID lpx)
{
    HANDLE hS;
    LONG lPrevious;
    hS = *(HANDLE *)lpx;
    while (!bThEnd) {
        WaitForSingleObject(hS, INFINITE);
        printf("スレッド0が%dを表示\n", i++);
        ReleaseSemaphore(hS, 1, &lPrevious);
    }
    WaitForSingleObject(hS, INFINITE);
    printf("スレッド0終了\n");
    ReleaseSemaphore(hS, 1, &lPrevious);
    SetEvent(hEvent[0]);
    return 0;
}
unsigned __stdcall mythread1(LPVOID lpx)
{
    HANDLE hS;
    LONG lPrevious;
    hS = *(HANDLE *)lpx;
    while (!bThEnd) {
        WaitForSingleObject(hS, INFINITE);
        printf("スレッド1が%dを表示\n", i++);
        ReleaseSemaphore(hS, 1, &lPrevious);
    }
    WaitForSingleObject(hS, INFINITE);
    printf("スレッド1終了\n");
    ReleaseSemaphore(hS, 1, &lPrevious);
    SetEvent(hEvent[1]);
    return 0;
}
これが、前章と同じ動作をするか確認してみてください。
Update Dec/04/2004 By Y.Kumei