[win32] Mutex

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

  1. Critical Section
  2. Mutex
  3. Semaphore

 

目次

1. Mutex をつかってみる
2. Mutex を使ってプロセス間の同期を行う
3. ***

 

1. Mutex をつかってみる

[概要]

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

[評価環境]

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

 

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

こちら、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 までしかカウントアップできませんでした。これはうまく動作しない例です。
この値はプログラムを実行するたびに代わります。

 

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

 

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

#include <iostream>     // std::cout, std::endl
#include <tchar.h>      // _tmain
#include <thread>       // std::thread
#include <Windows.h>    // HANDLE, CreateMutex, OpenMutex, WaitForSingleObject, ReleaseMutex, CloseHandle,


// グローバル変数
volatile long           count = 0;
LPCTSTR                 MutexName = _T("test_mutex");


void CountThread(const size_t iterations)
{
    int x;
    HANDLE      mutex = nullptr;

    mutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, MutexName);

    if (mutex != nullptr) {
        for (size_t i = 0; i < iterations; ++i)
        {
            DWORD ret = WaitForSingleObject(mutex, 500);

            switch (ret) {
            case WAIT_OBJECT_0:             // シグナル取得
                x = count;
                // 問題を発生させにくい場合は次の行のコメントアウトを外してください。
                //std::this_thread::yield();        // スレッドの処理明け渡し。c++11。
                x++;
                count = x;
                break;

            case WAIT_TIMEOUT:              // タイムアウト
                std::cout << "WAIT_TIMEOUT happend." << std::endl;
                break;

            case WAIT_ABANDONED:            // 他のオーナースレッド消失
                std::cout << "WAIT_ABANDONED happend." << std::endl;
                break;

            case WAIT_FAILED:               // 関数を失敗。原因は GetLastError で取得。
                std::cout << "WAIT_FAILED happend." << std::endl;
                std::cout << "GetLastError() : " << GetLastError() << std::endl;
                break;

            default:                        // タイムアウト、他
                // タイムアウト、異常発生、など
                std::cout << "Any other error happend." << std::endl;
                break;
            }

            ReleaseMutex(mutex);
        }
    }
}


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


    /*========*/
    /* 処理部 */
    /*========*/

    // Mutex を生成
    HANDLE      mutex = nullptr;
    mutex = CreateMutex(0, FALSE, MutexName);

    // スレッドを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;

    // Mutex を開放
    if (mutex != nullptr) {
        if (CloseHandle(mutex) == TRUE) {
            mutex = nullptr;
        }
    }


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

    return EXIT_SUCCESS;
}

 

[実行結果]

 

参考

 

 

2. Mutex を使ってプロセス間の同期を行う

[概要]

 

[評価環境]

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

 

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



	


サンプルプログラム

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

 

参考

 


記載: