この記事は2018年12月14日に投稿しました。
目次
Win32/64 APIシステムプログラミング―32/64ビットの共存
- 作者: 北山洋幸
- 出版社/メーカー: カットシステム
- 発売日: 2011/02/01
- メディア: 単行本
- クリック: 15回
- この商品を含むブログ (1件) を見る
1. はじめに
こんにちは、iOSのエディタアプリPWEditorの開発者の二俣です。
今回は業務で使用しているWin32APIでスレッド間の排他制御(クリティカルセクション)を行う方法についてです。
2. Win32APIでスレッド間の排他制御(クリティカルセクション)を行う
スレッド間の排他制御の方法はいろいろありますが、今回はWin32APIでスレッド間の排他制御(クリティカルセクション)を行う方法を紹介します。
クリティカルセクションによる排他制御の方法ですが、
クリティカルセクションオブジェクトを保持しているスレッドだけが処理を実行できる。
という方法になります。
使うAPIは次の4つになります。
1. クリティカルセクションの作成
書式
VOID InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
引数
lpCriticalSection
クリティカルセクションオブジェクト
戻り値
なし
2. クリティカルセクションの保持
書式
VOID EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
引数
lpCriticalSection
クリティカルセクションオブジェクト
戻り値
なし
3. クリティカルセクションの解放
書式
VOID LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
引数
lpCriticalSection
クリティカルセクションオブジェクト
戻り値
なし
4. クリティカルセクションの削除
書式
VOID DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
引数
lpCriticalSection
クリティカルセクションオブジェクト
戻り値
なし
実装例
グローバル変数でカウンタ(g_count)を用意します。
スレッド1、スレッド2でカウンタをそれぞれカウントアップ、カウントダウンして、その結果を表示します。
#include "stdafx.h" #include <windows.h> int g_count; // 排他制御するオブジェクト BOOL g_endThread1; // スレッド1終了フラグ BOOL g_endThread2; // スレッド2終了フラグ /** * @brief スレッド1処理 * カウンタをカウントアップして、その結果を表示します。 * * @param [in] param クリティカルセクションオブジェクト * @return 処理結果 */ DWORD WINAPI Thread1(LPVOID* param) { // クリティカルセクションオブジェクトを保持し、排他制御を開始します。 EnterCriticalSection((LPCRITICAL_SECTION)param); // 排他制御したい処理を記述します。 // スレッド1ではg_countをカウントアップして、結果を表示します。 while (g_count < 10) { printf("Thread1 count=%d\n", g_count); ++g_count; Sleep(13); } // クリティカルセクションオブジェクトを解放し、排他制御を終了します。 LeaveCriticalSection((LPCRITICAL_SECTION)param); // スレッド終了フラグを設定します。 g_endThread1 = TRUE; return 0; } /** * @brief スレッド2処理 * カウンタをカウントダウンして、その結果を表示します。 * * @param [in] param クリティカルセクションオブジェクト * @return 処理結果 */ DWORD WINAPI Thread2(LPVOID* param) { // クリティカルセクションオブジェクトを保持し、排他制御を開始します。 EnterCriticalSection((LPCRITICAL_SECTION)param); // 排他制御したい処理を記述します。 // スレッド2ではg_countをカウントダウンして、結果を表示します。 while (g_count > 0) { printf("Thread2 count=%d\n", g_count); --g_count; Sleep(7); } // クリティカルセクションオブジェクトを解放し、排他制御を終了します。 LeaveCriticalSection((LPCRITICAL_SECTION)param); // スレッド終了フラグを設定します。 g_endThread2 = TRUE; return 0; } /** * @brief メイン関数 * * @param [in] argc コマンドライン引数の数 * @param [in] argv[] コマンドライン引数 * @return 処理結果 */ int _tmain(int argc, _TCHAR* argv[]) { static CRITICAL_SECTION cs; // クリティカルセクションオブジェクト static HANDLE hThread1; // スレッド1ハンドル static HANDLE hThread2; // スレッド2ハンドル // グローバル変数を初期化します。 g_count = 5; g_endThread1 = FALSE; g_endThread2 = FALSE; // クリティカルセクションを初期化します。 InitializeCriticalSection(&cs); // スレッド1, スレッド2を生成し、開始します。 hThread1 = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)Thread1, (LPVOID)&cs, 0, NULL); hThread2 = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)Thread2, (LPVOID)&cs, 0, NULL); // スレッド1, スレッド2の終了を待ちます。 // 今回は処理を単純化するため、スレッド終了フラグが設定されるのをポーリングして待ちます。 while (TRUE) { if (g_endThread1 && g_endThread2) { break; } Sleep(10); } // スレッドハンドルをクローズします。 CloseHandle(hThread1); CloseHandle(hThread2); // クリティカルセクションを削除します。 DeleteCriticalSection((LPCRITICAL_SECTION)&cs); return 0; }
排他制御しないと、スレッド1とスレッド2が同時に実行されます。
排他制御すると、スレッド1の処理が終わってから、スレッド2の処理が実行されます。
3. おわりに
業務の通信処理で、機器のステータスリードしているスレッドとコマンドを送信するスレッドで、ステータスリードした結果を保持するグローバル変数を更新する際、排他制御するために使用しました。
猫でもわかるWindowsプログラミング 第4版 猫でもわかるシリーズ
- 作者: 粂井康孝
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2014/01/18
- メディア: Kindle版
- この商品を含むブログを見る
紹介している一部の記事のコードはGitlabで公開しています。
興味のある方は覗いてみてください。
私が勤務しているニューラルでは、主に組み込み系ソフトの開発を行っております。
弊社製品のハイブリッドOS Bi-OSは高い技術力を評価されており、特に制御系や通信系を得意としています。
私自身はiOSモバイルアプリやウィンドウズアプリを得意としております。
ソフトウェア開発に関して相談などございましたら、お気軽にご連絡ください。
また一緒に働きたい技術者の方も随時募集中です。
興味がありましたらご連絡ください。
EMAIL : info-nr@newral.co.jp / m-futamata@newral.co.jp
TEL : 042-523-3663
FAX : 042-540-1688