>> WINAPI入門トップに戻る
今回はプロセス・スレッド間の同期(イベント)について説明します。
過去二回は排他制御についてやってきましたが、
今回はプロセス・スレッド間で行う同期処理の仕組みを説明します。
イベントという仕組みを使って同期処理を行うのですが、
何かのイベントが発生したときに、シグナル・非シグナル状態を切り替える、というだけの
単純な仕組みです。
排他制御ではありませんので、同時に実行されてしまいます。
何かの動作をした時にシグナル・非シグナルを切り換える、というだけの単純なものです。
ただそれを、スレッドはもちろん、プロセス間でも共有できるというのが大きいですね。
まず、CreateEvent関数を使って、
イベントオブジェクトを作成します。
第一引数はNULLを指定するとデフォルト値が入ります。
第二引数はシグナルを非シグナル状態に戻すときに手動でするか(TRUE)自動でするか(FALSE)を指定します。
第三引数は初期のシグナル状態を指定します。TRUEでシグナル状態、FALSEで非シグナル状態を表します。
第四引数はイベントオブジェクトの名前を指定します。
成功すると、イベントオブジェクトのハンドルが返ります。
このイベントオブジェクトハンドルを取得するには、OpenEvent関数を実行します。
普通に使うには、
第一引数にEVENT_ALL_ACCESS、
第二引数にFALSE、
第三引数に作成したイベントオブジェクトの名前を指定します。
成功するとイベントオブジェクトのハンドルを返します。
今回は手動で非シグナル状態に戻すので、
CreateEvent関数の第二引数はTRUEの手動に、
第三引数は非シグナル状態で始めるのでFALSEを指定して作成します。
このイベントをシグナル状態にするにはSetEvent関数を使い、
非シグナル状態にするにはResetEvent関数を使います。
それぞれイベントオブジェクトのハンドルを指定します。
これらの関数を実行して、スレッド及びプロセス間でシグナル・非シグナルを切り替えるだけです。
以下のコードをご覧下さい。
このコードをコピーしてコンパイルし、ウィンドウを二つ立ち上げてください。
どちらかのウィンドウのクライアント領域を左クリックすると、
両方のウィンドウのカウンタが増加し始めます。
最初のCreateEventでは第二引数をTRUEにして非シグナル状態に戻すときは手動でするように設定し、
第三引数をFALSEにしてイベント作成時は非シグナル状態になるようにしています。
そして、WM_LBUTTONDOWNの左クリックのメッセージが発生した際に、
SetEvent関数を実行してシグナル状態にし、WaitForSingleObjectで待機していたプロセスの処理を
再開するようにしています。
排他制御しているわけではないので、同時に実行されます。
以上が、プロセス・スレッド間のイベントによる同期の説明になります。
次回は文字の描画(TextOut)について説明します。
>> 【文字の描画(TextOut)】に進む
>> WINAPI入門トップに戻る
プロセス・スレッド間の同期(イベント)
過去二回は排他制御についてやってきましたが、
今回はプロセス・スレッド間で行う同期処理の仕組みを説明します。
イベントという仕組みを使って同期処理を行うのですが、
何かのイベントが発生したときに、シグナル・非シグナル状態を切り替える、というだけの
単純な仕組みです。
排他制御ではありませんので、同時に実行されてしまいます。
何かの動作をした時にシグナル・非シグナルを切り換える、というだけの単純なものです。
ただそれを、スレッドはもちろん、プロセス間でも共有できるというのが大きいですね。
まず、CreateEvent関数を使って、
イベントオブジェクトを作成します。
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, // セキュリティ記述子
BOOL bManualReset, // リセットのタイプ
BOOL bInitialState, // 初期状態
LPCTSTR lpName // イベントオブジェクトの名前
);
LPSECURITY_ATTRIBUTES lpEventAttributes, // セキュリティ記述子
BOOL bManualReset, // リセットのタイプ
BOOL bInitialState, // 初期状態
LPCTSTR lpName // イベントオブジェクトの名前
);
第一引数はNULLを指定するとデフォルト値が入ります。
第二引数はシグナルを非シグナル状態に戻すときに手動でするか(TRUE)自動でするか(FALSE)を指定します。
第三引数は初期のシグナル状態を指定します。TRUEでシグナル状態、FALSEで非シグナル状態を表します。
第四引数はイベントオブジェクトの名前を指定します。
成功すると、イベントオブジェクトのハンドルが返ります。
このイベントオブジェクトハンドルを取得するには、OpenEvent関数を実行します。
HANDLE OpenEvent(
DWORD dwDesiredAccess, // アクセス権
BOOL bInheritHandle, // 継承オプション
LPCTSTR lpName // イベントオブジェクトの名前
);
DWORD dwDesiredAccess, // アクセス権
BOOL bInheritHandle, // 継承オプション
LPCTSTR lpName // イベントオブジェクトの名前
);
普通に使うには、
第一引数にEVENT_ALL_ACCESS、
第二引数にFALSE、
第三引数に作成したイベントオブジェクトの名前を指定します。
成功するとイベントオブジェクトのハンドルを返します。
今回は手動で非シグナル状態に戻すので、
CreateEvent関数の第二引数はTRUEの手動に、
第三引数は非シグナル状態で始めるのでFALSEを指定して作成します。
このイベントをシグナル状態にするにはSetEvent関数を使い、
非シグナル状態にするにはResetEvent関数を使います。
BOOL SetEvent(
HANDLE hEvent // イベントオブジェクトのハンドル
);
BOOL ResetEvent(
HANDLE hEvent // イベントオブジェクトのハンドル
);
HANDLE hEvent // イベントオブジェクトのハンドル
);
BOOL ResetEvent(
HANDLE hEvent // イベントオブジェクトのハンドル
);
それぞれイベントオブジェクトのハンドルを指定します。
これらの関数を実行して、スレッド及びプロセス間でシグナル・非シグナルを切り替えるだけです。
以下のコードをご覧下さい。
#include <windows.h> #include <stdio.h> #define MSG(m) {\ MessageBoxA(NULL,m,NULL,MB_OK);} //ウィンドウハンドル HWND hwnd; //インスタンスハンドル HINSTANCE hinst; //ウィンドウ横幅 #define WIDTH 500 #define HEIGHT 300 //グローバル変数count int count; DWORD WINAPI Thread1(LPVOID *data) { char buf[1000]; HANDLE h; //対象のイベントオブジェクトを取得 h=OpenEvent(EVENT_ALL_ACCESS,FALSE,"EVENT"); //シグナル状態になるまで待つ。 WaitForSingleObject(h,INFINITE); //非シグナル状態にする。 ResetEvent(h); while(count<1000){ //カウントをウィンドウタイトルに表示 sprintf(buf,"%d",count); SetWindowText(hwnd,buf); ++count; Sleep(20); } ExitThread(0); } LRESULT CALLBACK WinProc(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp) { static HANDLE th1,sh; switch(msg){ case WM_DESTROY: CloseHandle(th1); CloseHandle(sh); PostQuitMessage(0); return 0; case WM_CREATE: //イベントオブジェクト作成 sh=CreateEvent(NULL,TRUE,FALSE,"EVENT"); //スレッドを2個作成 th1=CreateThread(0,0,(LPTHREAD_START_ROUTINE)Thread1,NULL,0,NULL); return 0; case WM_LBUTTONDOWN: SetEvent(sh); return 0; } return DefWindowProc(hwnd,msg,wp,lp); } int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd) { MSG msg; WNDCLASS wc; wc.style=CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc=WinProc; wc.cbClsExtra=wc.cbWndExtra=0; wc.hInstance=hInstance; wc.hCursor=wc.hIcon=NULL; wc.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH); wc.lpszClassName="test"; wc.lpszMenuName=NULL; if(!RegisterClass(&wc)){ MSG("クラスの登録失敗"); return -1; } hwnd=CreateWindowA("test","テストウィンドウ",WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 0,0,400,400,NULL,NULL,hInstance,NULL); if(hwnd==NULL){ MSG("ウィンドウ作成失敗"); return -1; } //インスタンスハンドル hinst=hInstance; //エラーチェック用変数 int check; while(check=GetMessage(&msg,NULL,0,0)){ if(check==-1){ break; } DispatchMessage(&msg); } //クラス解放 UnregisterClass("test",hinst); return 0; }
このコードをコピーしてコンパイルし、ウィンドウを二つ立ち上げてください。
どちらかのウィンドウのクライアント領域を左クリックすると、
両方のウィンドウのカウンタが増加し始めます。
最初のCreateEventでは第二引数をTRUEにして非シグナル状態に戻すときは手動でするように設定し、
第三引数をFALSEにしてイベント作成時は非シグナル状態になるようにしています。
そして、WM_LBUTTONDOWNの左クリックのメッセージが発生した際に、
SetEvent関数を実行してシグナル状態にし、WaitForSingleObjectで待機していたプロセスの処理を
再開するようにしています。
排他制御しているわけではないので、同時に実行されます。
以上が、プロセス・スレッド間のイベントによる同期の説明になります。
次回は文字の描画(TextOut)について説明します。
>> 【文字の描画(TextOut)】に進む
>> WINAPI入門トップに戻る