この記事は2018年12月15日に投稿しました。
目次
Win32/64 APIシステムプログラミング―32/64ビットの共存
- 作者: 北山洋幸
- 出版社/メーカー: カットシステム
- 発売日: 2011/02/01
- メディア: 単行本
- クリック: 15回
- この商品を含むブログ (1件) を見る
1. はじめに
こんにちは、iOSのエディタアプリPWEditorの開発者の二俣です。
今回は業務で使用しているWin32APIでスレッド間の同期処理(イベント)を行う方法についてです。
2. Win32APIでスレッド間の同期処理(イベント)を行う
Win32APIでスレッド間の同期処理を行う方法はいくつかありますが、今回はイベントを使った方法を紹介します。
イベントを使用した同期方法ですが、
- 同期したいスレッドをイベント待ちする。
- 同期させるスレッドで、同期したいスレッドのイベント待ちを解除する。
という方法になります。
使うAPIは次の5つになります。
1. イベントオブジェクトを作成する
書式
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName );
引数
lpEventAttributes
セキュリティ記述子
子プロセスが取得したハンドルを継承するかどうか決定します。
NULLを指定すると継承されません。
bManualReset
非シグナル状態に、手動か自動で設定するか指定します。
TRUEの場合、自動
FASLEの場合、手動
bInitialState
初期状態をシグナル状態か非シグナル状態か設定します。
TRUEの場合、シグナル状態
FALSEの場合、非シグナル状態
lpName
イベントオブジェクト名
戻り値
成功した場合、イベントオブジェクトのハンドル
失敗した場合、NULL
2. イベントオブジェクトを取得する
書式
HANDLE OpenEvent( DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName );
引数
dwDesiredAccess
アクセス権を指定します
| アクセス権 | 説明 |
| :- | :- |
| EVENT_ALL_ACCESS | 全てのアクセス権を許可します。 |
| EVENT_MODIFY_STATE | SetEvent関数とResetEvent関数を使ってイベントの状態を変更を許可します。
| SYNCHRONIZE | 待機関数を使って、イベントがシグナル化を待機することを許可します。 |
hInheritHandle
ハンドルを継承可能にするかどうか指定します。
TRUEの場合、継承可能
FALSEの場合、継承不可
lpName
イベントオブジェクト名
戻り値
成功した場合、イベントオブジェクトのハンドル
失敗した場合、NULL
3. イベントをシグナル状態にする
書式
BOOL SetEvent( HANDLE hEvent );
引数
hEvent
イベントオブジェクトのハンドル
戻り値
成功した場合、0以外の値
失敗した場合、0
4. イベントを非シグナル状態にする
書式
BOOL ResetEvent( HANDLE hEvent );
引数
hEvent
イベントオブジェクトのハンドル
戻り値
成功した場合、0以外の値
失敗した場合、0
5. オブジェクトを待機する。
書式
DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds );
引数
hHandle
待機するオブジェクトのハンドル
dwMilliseconds
タイムアウト時間をミリ秒(ms)単位で指定します。
INIFINITを指定すると、オブジェクトがシグナル状態になるまで無限待ちします。
戻り値
値 | 意味 |
---|---|
WAIT_ABANDONED | 指定されたオブジェクトが、放棄されたミューテックスオブジェクトだった。 |
WAIT_OBJECT_0 | 指定したオブジェクトが非シグナル状態になった。 |
WAIT_TIMEOUT | タイムアウト時間が経過した。 |
実装例
#include "stdafx.h" #include <Windows.h> #define EVENT_NAME _T("EVENT") // イベントオブジェクト名 int g_count; // 同期させるカウンタ BOOL g_endThread; // スレッド終了フラグ /** * @brief スレッド処理 * 同期処理前後のカウンタ値を表示します。 * * @param [in] param パラメータ(未使用) * @return 処理結果 */ DWORD WINAPI Thread(LPVOID* param) { // メイン関数のカウントアップ処理を進めるため、少し待ちます。 Sleep(10); // この時点のカウンタ値を表示します。 // イベント待ちしていないため、タイミングによりカウンタ値は異なります。 printf("Thread count=%d\n", g_count); // イベントオブジェクトを取得します。 HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME); // イベントオブジェクトがシグナル状態になるまで待ちます。 WaitForSingleObject(hEvent, INFINITE); // イベントオブジェクトを非シグナル状態にします。 // 再度イベント待ちするために必要な処理ですが、 // 今回は以降でイベント待ちしないので、処理しなくても構いません。 ResetEvent(hEvent); // この時点でのカウンタ値を表示します。 // イベント待ちしているため、メイン関数で最大値(10)にカウントアップされています。 printf("Thread count=%d\n", g_count); // スレッド終了フラグを設定します。 g_endThread = TRUE; return 0; } /** * @brief メイン関数 * * @param [in] argc コマンドライン引数の数 * @param [in] argv[] コマンドライン引数 * @return 処理結果 */ int _tmain(int argc, _TCHAR* argv[]) { // グローバル変数を初期化します。 g_count = 0; g_endThread = FALSE; // イベントオブジェクトを作成します。 HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, EVENT_NAME); // スレッドを作成し、開始します。 HANDLE hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)Thread, NULL, 0, NULL); // カウンタ値をカウントアップします。 while (g_count < 10) { ++g_count; Sleep(10); } // イベントオブジェクトをシグナル状態にし、スレッドのイベント待ちを解除します。 SetEvent(hEvent); // スレッドの終了を待ちます。 // 今回は処理を単純化するため、スレッド終了フラグが設定されるのをポーリングして待ちます。 while (TRUE) { if (g_endThread) { break; } Sleep(10); } // スレッドのハンドルをクローズします。 CloseHandle(hThread); // イベントオブジェクトのハンドルをクローズします。 CloseHandle(hEvent); return 0; }
3. おわりに
WaitForSingleObject関数は、イベント専用の関数ではなく、他のオブジェクトを待機するときにも使用します。
業務の通信処理で、設定コマンドを送信し、その設定値になるまで待機する必要がありました。
設定値は別スレッドのステータスリード処理で取得していました。
そのため今回のイベントを使い、ステータスがリードされたタイミングでイベントをセットし、送信コマンド側の待機を解除することにしました。
猫でもわかる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