同期オブジェクト(クリティカルセクション)によるスレッドの同期



複数のスレッドを同時に実行する場合、その同期を取らないと同じグローバル領域を使用している時、結果に不具合が生じる場合がある。
また、スレッド中にループ処理をさせるとCPU使用率が100%となり、他の処理を受け付けずスレッドの意味を成さなくなる。
このような問題を解決するために、Windowsには以下の同期オブジェクトが存在します。

 1.クリティカル・セクション
 2.セマフォ
 3.ミューテックス

ここでは、クリティカル・セクションの例を示します。

クリティカル・セクションを使用するための関数は以下の通りです。詳細は、MSDNを参照して下さい。

    InitializeCriticalSection()    //クリティカル・セクションの初期化

    EnterCriticalSection()     //クリティカル・セクションの開始

    LeaveCriticalSection()         //クリティカル・セクションの解放

    DeleteCriticalSection()        //クリティカル・セクションの削除


【サンプル】


//スレッド操作用
int EndFlagEx;
char strResultEx[10];

//スレッド関数のプロトタイプ宣言
void Thread1Ex(CListBox* LstBox);
void Thread2Ex(CListBox* LstBox);
void Thread3Ex(CListBox* LstBox);

//クリティカルセクション
CRITICAL_SECTION g_critical_section;


// 3つのスレッドを実行する
void CThreadCritEx::Start3Threads(CListBox* LstBox)
{
    HANDLE hThread1, hThread2, hThread3;
    DWORD threadID1, threadID2, threadID3;

    //クリティカルセクションを初期化する
    InitializeCriticalSection(&g_critical_section);

    //スレッドの実行
    EndFlagEx = 0;
    hThread1 = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)Thread1Ex,
                           (VOID *)LstBox, 0, &threadID1);    
    hThread2 = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)Thread2Ex,
                           (VOID *)LstBox, 0, &threadID2);
    hThread3 = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)Thread3Ex,
                           (VOID *)LstBox, 0, &threadID3);
}

// 3つのスレッドを終了する
void CThreadCritEx::End3Thread(void)
{
    EndFlagEx = 1;
    DeleteCriticalSection(&g_critical_section);
}

//スレッド1
void Thread1Ex(CListBox* LstBox)
{
    unsigned int i, cnt;
    char PutArea[20];

    while(1){
        if(EndFlagEx == 1)
            break;

        //クリティカルセクションに入る
        EnterCriticalSection(&g_critical_section);

        strcpy(PutArea,"Thread1:");
        cnt = 10;
        for(i = 0; i < cnt - 1; i++){
            strResultEx[i] = '@';
        }
        strResultEx[strlen(strResultEx)] = '\0';

        strcat(PutArea, strResultEx);
        LstBox->AddString(PutArea);
        ZeroMemory(PutArea,20);

        //クリティカルセクション
        LeaveCriticalSection(&g_critical_section);
    }

    //Threadを終了
    ExitThread(0);

}

//スレッド2
void Thread2Ex(CListBox* LstBox)
{
    unsigned int i, cnt;
    char PutArea[20];

    while(1){
        if(EndFlagEx == 1)break;

        //クリティカルセクションに入る
        EnterCriticalSection(&g_critical_section);

        strcpy(PutArea,"Thread2:");

        cnt = 10;
        for(i = 0; i < cnt - 1; i++){
            strResultEx[i] = '#';
        }
        strResultEx[strlen(strResultEx)] = '\0';

        strcat(PutArea, strResultEx);
        LstBox->AddString(PutArea);
        ZeroMemory(PutArea,20);

        //クリティカルセクション
        LeaveCriticalSection(&g_critical_section);

    }

    //Threadを終了
    ExitThread(0);
}

//スレッド3
void Thread3Ex(CListBox* LstBox)
{
    unsigned int i, cnt;
    char PutArea[20];


    while(1){
        if(EndFlagEx == 1)break;

        //クリティカルセクションに入る
        EnterCriticalSection(&g_critical_section);

        strcpy(PutArea,"Thread3:");

        cnt = 10;
        for(i = 0; i < cnt - 1; i++){
            strResultEx[i] = '!';
        }
        strResultEx[strlen(strResultEx)] = '\0';

        strcat(PutArea, strResultEx);
        LstBox->AddString(PutArea);
        ZeroMemory(PutArea,20);

        //クリティカルセクション
        LeaveCriticalSection(&g_critical_section);

    }

    //Threadを終了
    ExitThread(0);
}

[サンプル・ソース]

[備考]
リンクしてあるサンプル・ソースには、同期オブジェクトの代わりにSleep()を使用してあるコードも含まれています。Sleep()を使用しても良さそうに感じますがCPU効率、処理効率を考えると同期オブジェクトを使用することが望ましいです。
その違いを実際に比べて頂き、スレッドの扱いの参考になれば幸いです。


(注意)
このサンプルは、Visual C++ 2003.Net, WinXPで作成されています。