>> 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入門トップに戻る













