[win32] Critical Section

Windows の win32 における代表的な同期処理には以下の3つの手法があります。
ここでは下記から「1. Critical Section」について記載します。

  1. Critical Section
  2. Mutex
  3. Semaphore

 

目次

1. CriticalSection を使ってみる
2. ***
3. ***

 

1. CriticalSection を使ってみる

[概要]

まずは CriticalSection を使用するシンプルな例を示します。

 

[評価環境]

コンパイラ : Visual Studio 2019 pro., Version 16.4.4
OS: Windows10 home, Version 1909

 

最初に、うまく動かない例を示します。そして次に、CriticalSectionを使用することで意図通りに動作する例を示します。

こちら、BadExample.cpp はうまく動かない例です。
4つのスレッドがそれぞれ25,000回のインクリメントをするので、最後に count は100,000になることを期待するプログラムです。しかし同期処理がないために意図する動作をしない、という例です。

[BadExample.cpp]

#include <iostream>     // std::cout, std::endl
#include <tchar.h>      // _tmain
#include <thread>       // std::thread

// グローバル変数
volatile long           count = 0;

void CountThread(const size_t iterations)
{
    int x;

    for (size_t i = 0; i < iterations; ++i)
    {
        x = count;
        // 問題を発生させにくい場合は次の行のコメントアウトを外してください。
//      std::this_thread::yield();      // スレッドの処理明け渡し。c++11。
        x++;
        count = x;
    }
}


int _tmain(int argc, char* argv[])
{
    const size_t    numThreads = 4;
    std::thread     countThreads[numThreads];

    /*========*/
    /* 処理部 */
    /*========*/
    // スレッドを4つ作成
    for (size_t i = 0; i < numThreads; ++i)
    {
        countThreads[i] = std::thread(CountThread, 25000);
    }

    // 4つのスレッドが終わるのを待つ
    for (size_t i = 0; i < numThreads; ++i)
    {
        countThreads[i].join();
    }

    // 4つのスレッドが終了したのちの count の値をチェック
    std::cout << "count = " << count << std::endl;
    
    /*========*/
    /* 後処理 */
    /*========*/
    // [Enter]キー 入力待ち
    {
        std::cout << "HIT [Enter] KEY ";
        int ch = getchar();
    }

    return EXIT_SUCCESS;
}

 

期待する 100,000 に対して 65,703 までしかカウントアップできませんでした。これはうまく動作しない例です。
この値はプログラムを実行するたびに代わります。

 

次に CriticalSection により同期処理を加えた例を示します。

 

#include <windows.h>    // CRITICAL_SECTION, InitializeCriticalSection, DeleteCriticalSection, EnterCriticalSection, LeaveCriticalSection
#include <iostream>     // std::cout, std::endl
#include <tchar.h>      // _tmain
#include <thread>       // std::thread
#include <atomic>       // std::atomoic

// グローバル変数
volatile long           count = 0;
CRITICAL_SECTION        critSec;

void CountThread(size_t iterations)
{
    int x;

    for (size_t i = 0; i < iterations; ++i)
    {
        EnterCriticalSection(&critSec);
        {
            x = count;
            x++;
            count = x;
        }
        LeaveCriticalSection(&critSec);
    }
}

int _tmain(int argc, char* argv[])
{
    const size_t    numThreads = 4;
    std::thread     countThreads[numThreads];

    InitializeCriticalSection(&critSec);

    {
        // スレッドを4つ作成
        for (size_t i = 0; i < numThreads; ++i)
        {
            countThreads[i] = std::thread(CountThread, 25000);
        }

        // 4つのスレッドが終わるのを待つ
        for (size_t i = 0; i < numThreads; ++i)
        {
            countThreads[i].join();
        }

        // 4つのスレッドが終了したのちの count の値をチェック
        std::cout << "count = " << count << std::endl;
    }

    DeleteCriticalSection(&critSec);

    /*========*/
    /* 後処理 */
    /*========*/
    {
        std::cout << "HIT [Enter] KEY ";
        int ch = getchar();
    }

    return EXIT_SUCCESS;
}

 

今度はちゃんと 100,000 になりました。

 

参考

 

 

2. ***

[概要]

 

[評価環境]

コンパイラ : Visual Studio 2019 pro., Version 16.4.0
OS: Windows10 home, Version 1909

 

[プログラムソース "***.cpp"]



	


サンプルプログラム

サンプルプログラム ダウンロード

 

参考

 


記載: