Version 14.08
セマフォを使ってみる!
「前々回はミューテックスを使用してみました」
『バトンをパスする、って感じだったね』
「今回はセマフォという同期オブジェクトを使用してみます」
セマフォ ( Semaphore )
『〈信号機〉って意味なんだよね』
「そうなんだけど、それほどそこにこだわらなくてもいいかも。セマフォは
Version 14.04 ( No.271 ) で説明したように」
・バトンが複数個ある
「のが特徴」
『ミューテックスはひとつ、セマフォは複数』
「バトンの例でそのまま説明するなら」
・ミューテックス:1コースのみ
・セマフォ :コースが複数ある
「つまり、セマフォで4つバトンを用意するとすれば、4コース分用意でき
て、その場合、同時に走れる選手の数は?」
『4人?』
「そういうこと。つまり、〈バトンの数だけ同時に選手が走られる〉ってこ
とです」
『ミューテックスはひとつだったけど、セマフォは複数……』
「ま、実際に例を見てみようか。ミューテックスで作った ProcessA とかを
流用して」
namespace B1
{
HANDLE g_hSemaphore = NULL;
const char *const SEMAPHORE_NAME= "ProcessesSemaphore";
// セマフォを作成します。
void Create()
{
g_hSemaphore = CreateSemaphore( NULL, 2, 2, SEMAPHORE_NAME );
TRACE( "%x\n", g_hSemaphore );
}
// セマフォを取得します。
void Open()
{
g_hSemaphore
= OpenSemaphore
( SEMAPHORE_ALL_ACCESS, FALSE, SEMAPHORE_NAME );
TRACE( "%x\n", g_hSemaphore );
}
// セマフォを監視して待機状態に入ります。
void Wait()
{
TRACE( "待機開始。\n" );
DWORD dwResult = WaitForSingleObject( g_hSemaphore, 120000 );
if( dwResult == WAIT_TIMEOUT )
{
TRACE( "タイムアウト。\n" );
}
else
{
TRACE( "待機終了。\n" );
}
}
// セマフォを手放します。
void Release()
{
LONG lCount = 0;
ReleaseSemaphore( g_hSemaphore, 1, &lCount );
TRACE( "現在のカウント: %d\n", lCount );
}
// セマフォを削除します。
void Close()
{
CloseHandle( g_hSemaphore );
}
}
「というプログラムを書いてください。各関数は、前と同じようにボタンの
イベントハンドラ等から呼ぶようにしてください」
『はーい』
「では、ひとつずつ見ていきます」
HANDLE g_hSemaphore = NULL;
const char *const SEMAPHORE_NAME= "ProcessesSemaphore";
「これはミューテックスの時と同じ」
『セマフォのハンドルと、セマフォの名前ね』
「では次、今回一番大事なセマフォの作成箇所」
// セマフォを作成します。
void Create()
{
g_hSemaphore = CreateSemaphore( NULL, 2, 2, SEMAPHORE_NAME );
TRACE( "%x\n", g_hSemaphore );
}
「セマフォは CreateSemaphore() という API で作成します」
『ってゆーかミューテックスとほとんど同じね』
「第1引数はセキュリティ関係なので NULL でOK。第4引数はミューテッ
クスと同じくセマフォの名前を渡してください」
『ってことは、第2引数と第3引数が重要?』
「そういうこと」
g_hSemaphore = CreateSemaphore
( NULL
, 2 // カウントの初期値。
, 2 // カウントの最大値。
, SEMAPHORE_NAME
);
『? 第2引数の〈カウントの初期値〉って 0 じゃないの?』
「そこがポイント。まず、一番重要なのは」
・セマフォで管理するカウント=空いているバトンの数
「ってこと」
『空いているバトン……つまり、誰も持って走ってないバトンの数、ってこ
と?』
「そういうこと。このセマフォを作る段階では、まだなにも処理をしていま
せん。ということは、空いているバトンの数は?」
『 2 !』
「というわけ」
『だから初期値は 2 なのねー』
「さて、ここでは2個のバトンを用意しただけ。実際にバトンを〈持つ〉必
要があります。そのためにはまず」
// セマフォを取得します。
void Open()
{
g_hSemaphore
= OpenSemaphore
( SEMAPHORE_ALL_ACCESS, FALSE, SEMAPHORE_NAME );
TRACE( "%x\n", g_hSemaphore );
}
「でセマフォのハンドルを取得します。ただし、この段階ではまだバトンを
持っていません」
『ややこしい……』
「セマフォとバトンをごっちゃにしちゃってるからね……まぁ、たとえるな
らバトンが置いてあるテーブルにまで来た、っていう状態かな」
『よけいわかんない』
「う”……さて、それではバトンを所持します」
// セマフォを監視して待機状態に入ります。
void Wait()
{
TRACE( "待機開始。\n" );
DWORD dwResult = WaitForSingleObject( g_hSemaphore, 120000 );
if( dwResult == WAIT_TIMEOUT )
{
TRACE( "タイムアウト。\n" );
}
else
{
TRACE( "待機終了。\n" );
}
}
『あ、監視するのがバトンを持つってこと?』
「そういうことになります。この WaitForSingleObject() を呼ぶと、カウ
ントによってセマフォの動作が異なります」
○セマフォのカウントが1以上:
・シグナルはオン
・カウントが1減る
○セマフォのカウントがゼロ:
・シグナルはオフ
・カウントの増減はなし
『???』
「これをわかりやすく書くと」
○バトンの数が1以上:
・バトンをもらって動作開始
・バトンの数が1減る
○バトンの数がゼロ:
・バトンがないので、バトンが空くまで待ちます。
・バトンはゼロのまま
『おお!』
「つまり、カウントが 0 の時には WaitForSingleObject() は監視を続けま
す。また、カウントが 1 以上ならすぐに返ってそのまま処理を続けられま
す」
『バトンを持って処理をするってわけねー』
「そして、処理が終わったらバトンを返します」
// セマフォを手放します。
void Release()
{
LONG lCount = 0;
ReleaseSemaphore( g_hSemaphore, 1, &lCount );
TRACE( "現在のカウント: %d\n", lCount );
}
「 ReleaseSemaphore() は WaitForSingleObject() で減らしたセマフォの
カウントを逆に増やします」
『つまり、バトンを返すってこと?』
「そういうこと。ただ、さっきの WaitForSingleObject() で待っている場
合には、バトンがひとつ空くから」
○バトンの数が1以上:
・バトンをもらって動作開始
・バトンの数が1減る
「となって WaitForSingleObject() から返ります」
『つまり、バトンを手渡したってことね』
「そういうこと」
/*
Preview Next Story!
*/
『相変わらず話だけ聞くとわかりやすいね』
「話は単純だからね」
『……人騙すの得意じゃない?』
「ななな、何を???」
『いやなんとなく』
「ちゃんと裏付けも説明するんだから、騙してるわけじゃないよ」
『ホントに〜?』
「というわけで次回」
< Version 14.09 セマフォで排他処理 >
『につづく!』
「というわけでプログラムでちゃんと証明してみます」
『なら騙されっぱなしでもいいかも』
「なんでー?」