Windows の win32 における代表的な同期処理には以下の3つの手法があります。
ここでは下記から「2. Mutex」について記載します。
1. Mutex をつかってみる
2. Mutex を使ってプロセス間の同期を行う
3. ***
[概要]
まずは 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;
}
[実行結果]
[概要]
[評価環境]
コンパイラ : | Visual Studio 2019 pro., | Version 16.4.0 |
OS: | Windows10 home, | Version 1909 |
[プログラムソース "***.cpp"]
サンプルプログラム ダウンロード
記載: