はじめに
VC++でマルチスレッドプログラミングのサンプルコードを書いてみました。
以下のページに書かれているサンプルプログラムがVS2013にコピーペーストするだけで実行できて、とても参考になりました。
スレッドの作成
スレッドの作成は、_beginthreadex関数を使います。
hThread[0] = (HANDLE)_beginthreadex(NULL, stackSize, IncrementCounter, NULL, 0, NULL);
第1引数 | start_address | 新規スレッドの実行を起動するルーチンの開始アドレス。 |
第2引数 | stack_size | 新規スレッドのスタック サイズまたは 0。 |
第3引数 | arglist | 新規スレッドに渡される引数リストまたは NULL。 |
第4引数 | security | SECURITY_ATTRIBUTES 構造体へのポインタ。 |
第5引数 | initflag | 新規スレッドの初期状態 (実行中は 0、一時停止中は CREATE_SUSPENDED)。 |
第6引数 | thrdaddr | スレッド識別子を受け取る 32 ビット変数へのポインタ。 |
参考にしたページでは、_beginthreadを使っていましたが、windowsは_beginthreadex推奨みたいなので、こちらを使いました。
解説
以下のコードは、2つのスレッドを作成しています。
- スレッド1 変数Aと変数Bをそれぞれカウントアップしてきます。
- スレッド2 変数Aと変数Bのカウンタをゼロにリセットします。
このプログラムを実行すると、以下のような表示になります。
スレッド1は変数Aを0〜9まで1つずつカウントアップできているように見えます。一方、変数Bも同じようにカウントアップしているはずなのですが、ゼロ(0)で表示されています。
これはスレッド1がカウントアップしている最中にスレッド2が変数Bを書き換えているために起こります。
変数AはWaitForSingleObjectを使い、他のスレッドを待たせているため割り込みが発生せずに、カウントアップができています。
サンプルコード
#include "stdafx.h" #include <windows.h> #include <process.h> HANDLE hMutex; //ミューテックスのハンドル int count_A = 0; //ミューテックスで排他制御された変数 int count_B = 0; //排他制御されていない変数 unsigned __stdcall IncrementCounter(LPVOID pParam) { while (1) { // hMutexオブジェクトがシグナル状態になるまで待つ(待ち時間は無限INFINITE) DWORD ret = WaitForSingleObject(hMutex, INFINITE); if (ret == WAIT_FAILED) { printf("WaitForMultipleObjectsでエラー(%d)", GetLastError()); return 0; } // count_Aのカウントアップ printf("count_A:"); for (int i = 0; i < 10; i++) { printf("%d:", count_A); count_A++; Sleep(500); // 0.5秒スリープ } printf("\n"); // ミューテックスの解放 ReleaseMutex(hMutex); // count_Bのカウントアップ printf("count_B:"); for (int i = 0; i < 10; i++) { printf("%d:", count_B); count_B++; Sleep(500); // 0.5秒スリープ } printf("\n"); } } unsigned __stdcall resetCounter(LPVOID pParam) { while (1) { // hMutexオブジェクトがシグナル状態になるまで待つ(待ち時間は無限INFINITE) DWORD ret = WaitForSingleObject(hMutex, INFINITE); if (ret == WAIT_FAILED) { printf("WaitForMultipleObjectsでエラー(%d)", GetLastError()); return 0; } count_A = 0; // ミューテックスの解放 ReleaseMutex(hMutex); count_B = 0; } } // メイン関数 int _tmain(int argc, _TCHAR* argv[]) { HANDLE hThread[2] = { 0 }; //ハンドル //ミューテックス生成 hMutex = CreateMutex(NULL, FALSE, NULL); if (::GetLastError() == ERROR_ALREADY_EXISTS) { printf("ミューテックスの作成に失敗しました。(%d)", GetLastError()); return 0; } int stackSize = 0; // スレッド1の作成 hThread[0] = (HANDLE)_beginthreadex(NULL, stackSize, IncrementCounter, NULL, 0, NULL); if (hThread[0] == 0) { printf("スレッドの作成に失敗しました。(%d)", GetLastError()); return 0; } // スレッド2の作成 hThread[1] = (HANDLE)_beginthreadex(NULL, stackSize, resetCounter, NULL, 0, NULL); if (hThread[1] == 0) { printf("スレッドの作成に失敗しました。(%d)", GetLastError()); return 0; } //スレッド1、2終了待ち DWORD ret = WaitForMultipleObjects(2, hThread, TRUE, INFINITE); if (ret == WAIT_FAILED) { printf("WaitForMultipleObjectsでエラー(%d)", GetLastError()); return 0; } //ハンドルクローズ CloseHandle(hThread[0]); CloseHandle(hThread[1]); CloseHandle(hMutex); return 0; }
関連記事
- 【cppcheck】warning: Opposite conditions in nested 'if' blocks lead to a dead code block.
- 【cppcheck】warning: Redundant assignment of 'xxx' to itself.
- 【cppcheck】error: Memory is allocated but not initialized: xxx
- 【cppcheck】(warning) %d in format string (no. 1) requires 'int' but the argument type is 'ULONG {aka unsigned long}'.
- 【VC++】OCIを使ってOracle DBに接続するコード書いたよ。