mikan's technical note

仕事&趣味で実験した小技の備忘録です(Linux,windows,DOS等)

MENU

【C言語】マルチスレッドの排他処理

Win32API
CreateEvent()
SetEvent
CreateThread()
WaitForSingleObject()
WaitForMultipleObjects
を使用したマルチスレッド排他処理の動作確認です。

//
// 【スレッドで順次処理(順不同)】※Windows7で動作確認済
// (c) 2017 mikan
// ※使用にあたっては利用者の自己責任でお願いします。
// ※参考文献 Advaned Windows 改訂第4版 Jeffrey Richter
//

#include "stdafx.h"
#include "windows.h"

#define   MAX_RUN_THREAD_NUM    32        // 作成するスレッドの数

int       gCount;                         // カウンタ
int       gNumber[MAX_RUN_THREAD_NUM];    // スレッド番号
char      gName[256];                     // スレッド名
HANDLE    ghEvent;                        // イベントハンドル

// ---------- 状態表示 ----------
void print_status()
{
    int     i;

    printf("%s:", gName);
    for (i = 0; i < MAX_RUN_THREAD_NUM; i++) {
        printf("%2d,", gNumber[i]);
    }
    printf("count=%2d\n", gCount);
}

// ---------- スレッド ----------
int Thread(PVOID pvParam)
{
    int     *n;

    // イベントシグナル待ち(30秒でタイムアウト)
    DWORD dw = WaitForSingleObject(ghEvent, 30 * 1000);
    if (dw == WAIT_FAILED || dw == WAIT_TIMEOUT) {
        return -1;
    }

    n = (int*)(pvParam);
    gNumber[gCount] = *n;
    gCount++;
    sprintf(gName, "Thread%02d", *n);

    // 状態表示
    print_status();

    //Sleep(200);    // debug用

    // イベントシグナル
    SetEvent(ghEvent);

    return 0;
}

// ---------- 親スレッド ----------
int ThreadCtrl()
{
    int     x[MAX_RUN_THREAD_NUM];
    int     i;
    DWORD   dwThreadId[MAX_RUN_THREAD_NUM];
    HANDLE  hThread[MAX_RUN_THREAD_NUM];

    // 初期化
    gCount = 0;
    memset(gNumber, 0x00, sizeof(gNumber));
    memset(gName, 0x00, sizeof(gName));
    memset(hThread, 0x00, sizeof(hThread));
    for (i = 0; i < MAX_RUN_THREAD_NUM; i++) {
        x[i] = i+1;
    }

    // 自動リセットイベント作成(いずれかのスレッド1つだけが、シグナルを検出)
    ghEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

    // 手動リセットイベント作成(全てのスレッドが、同時にシグナルを検出)
    // ghEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    // スレッド作成
    for(i=0; i<MAX_RUN_THREAD_NUM; i++) {
        hThread[i] = CreateThread(
                         NULL,
                         0,
                         (PTHREAD_START_ROUTINE)Thread,
                         (PVOID)&x[i],
                         0,
                         &dwThreadId[i]);
    }

    // イベントシグナル
    SetEvent(ghEvent);

    // 全スレッド終了待ち
    DWORD dw = WaitForMultipleObjects(
                   MAX_RUN_THREAD_NUM,
                   hThread,
                   TRUE,
                   50 * 1000); // 50秒でタイムアウト

    // スレッドハンドルクローズ
    for (i = 0; i < MAX_RUN_THREAD_NUM; i++) {
        CloseHandle(hThread[i]);
    }

    if (dw == WAIT_FAILED || dw == WAIT_TIMEOUT) {
        return -1;
    }

    return 0;
}

// ---------- メイン ----------
void main()
{
    int     x;
    HANDLE  hThreadCtrl;
    DWORD   dwThreadCtrlId;
    DWORD   dwStart;
    
    dwStart = GetTickCount();

    printf("開始\n");

    // 親スレッド作成
    hThreadCtrl = CreateThread(
                      NULL,
                      0,
                      (PTHREAD_START_ROUTINE)ThreadCtrl,
                      (PVOID)&x,
                      0,
                      &dwThreadCtrlId);

    // 親スレッドの終了待ち(60秒でタイムアウト)
    DWORD dw = WaitForSingleObject(hThreadCtrl, 60 * 1000);

    // 親スレッドハンドルクローズ
    CloseHandle(hThreadCtrl);

    printf("終了\n");
    printf("経過時間=%d ms\n", GetTickCount() - dwStart);

    if (dw == WAIT_FAILED || dw == WAIT_TIMEOUT) {
        ;
    }

    return;
}

【実行結果】
※本PGでは各スレッドの起動順序はOSまかせとなります
f:id:myerss555:20170709051413j:plain

【手動リセットイベントで全スレッドを同時実行した結果】
※カウンタを正しくインクリメント出来ない可能性あり
f:id:myerss555:20170709052211j:plain