Version 14.09
セマフォで排他処理
「前回はセマフォを使ったプログラムを紹介しました」
『ミューテックスと違ってバトンはふたつ、ってことだったね』
「というわけで、今回は実際にそれを試してみます。ミューテックスの時、
つまり Version 14.06 ( No.273 ) の時と同じように、
ProcessA ProcessB ProcessC の3つのプロジェクトを用意して」
『ミューテックスの時のをそのまま使ってもいい?』
「もちろん。同じようにボタンを5つずつ作って」
Create() // セマフォを作成します。
Open() // セマフォを取得します。
Wait() // セマフォを監視して待機状態に入ります。
Release() // セマフォを手放します。
Close() // セマフォを削除します。
「を呼ぶようにしてください」
『この辺も同じね』
「さて、では実際に試してみます。前回のプログラムでは」
// セマフォを作成します。
void Create()
{
g_hSemaphore = CreateSemaphore( NULL, 2, 2, SEMAPHORE_NAME );
TRACE( "%x\n", g_hSemaphore );
}
「としました。つまり、バトンの数は2、最初の空いている数もその2、と
いうことになります」
『バトンは2本ね』
「今、 ProcessA ProcessB ProcessC の3つのプロセスを使うので、当然ひ
とつ余ります」
『余ると?』
「余ると、バトンが空くのを待つことになります。余らなかったふたつのプ
ロセスは、バトンをもらったらすぐに処理をします」
『ん〜?』
「というわけで実際に試してみましょう。まず」
ProcessA::プロセス実行
ProcessB::プロセス実行
ProcessC::プロセス実行
「と、まぁ普通に3つともデバッグ実行してください」
『はーい』
「次に
ProcessA::Create()
ProcessB::Open()
ProcessC::Open()
「と、 ProcessA はセマフォを作成して、残りのふたつはそのセマフォを開
いてください」
『名前が同じだから、同じセマフォを開くんだね』
「そういうこと。これで準備はできました。それでは同期のテストを開始し
ます。まず、現在のバトンの状況はこうなっています」
バトン1:空き
バトン2:空き
『バトン2本とも空いてるわけね』
「次に」
ProcessA::Wait()
「と、 ProcessA はセマフォを監視してください」
『ほい。……あれ? 待たないよ? あっという間に待機終了。しちゃった
よ?』
「それがセマフォ、ってこと。前回説明したでしょ」
○バトンの数が1以上:
・バトンをもらって動作開始
・バトンの数が1減る
○バトンの数がゼロ:
・バトンがないので、バトンが空くまで待ちます。
・バトンはゼロのまま
『ああ! バトンは2本ある、そのうちの1本もらって動作開始、動作開始
ってことは待たないってことだから、 WaitForSingleObject() からすぐに
返ってきちゃうんだ!』
「そういうこと。だから、現在の状況は
バトン1:ProcessA 所有中
バトン2:空き
ProcessA::バトン1所有中(よって処理中)
ProcessB::何もしてません
ProcessC::何もしてません
「という状況」
『バトン1が ProcessA に渡って、 ProcessA はそのバトン受け取ってなん
かしてるはず、ってわけね』
「次に」
ProcessB::Wait()
「と、 ProcessB もセマフォを監視してください」
『これもバトン取っちゃうからすぐに返ってくるんだよね。うん、返ってき
た』
「現在の状況はこうなります」
バトン1:ProcessA 所有中
バトン2:ProcessB 所有中
ProcessA::バトン1所有中(よって処理中)
ProcessB::バトン2所有中(よって処理中)
ProcessC::何もしてません
「この状態では、バトンの空きがありません」
『2本とも使われてるわけね』
「イメージとしては、トラック2つの競技場を、選手ふたりがバトンを持っ
て全力疾走してるイメージ」
『全然疾走してないけど』
「さて、この例で行くと ProcessC はもうひとりの走者ということになりま
す。ですが」
『バトンがないから走れない!』
「そういうこと」
ProcessC::Wait()
「として、 ProcessC もセマフォを監視してください」
『ほい。お、今度は返ってこない! ずっと待機開始。のままだ』
「現在の状態はこうです」
バトン1:ProcessA 所有中
バトン2:ProcessB 所有中
ProcessA::バトン1所有中(よって処理中)
ProcessB::バトン2所有中(よって処理中)
ProcessC::バトンが空くのを待っています
「バトンは2本しかありません。が、すでに ProcessA と ProcessB が持っ
ているため、 ProcessC はバトンが空くのを待っています」
『だから WaitForSingleObject() から返ってこないわけね』
「そこで、バトンをひとつ手放します」
ProcessA::Release()
「 ReleaseSemaphore() はセマフォのカウントを減らす、つまりバトンを返
す関数です」
『これを呼ぶと……あ、 ProcessC が待機終了。になった!』
「この瞬間を見てみましょう。まず、 ReleaseSemaphore() が呼ばれたこと
でバトンがひとつ空きます」
バトン1:空き
バトン2:ProcessB 所有中
ProcessA::何もしてません(バトン1を手放したため)
ProcessB::バトン2所有中(よって処理中)
ProcessC::バトンが空くのを待っています
「すると、一番最初に待機していたプロセスに、シグナルがオンになったこ
とが伝えられます」
バトン1:空き→→→→→→→→→→→→→→→→→
バトン2:ProcessB 所有中 ↓
ProcessA::何もしてません ↓
ProcessB::バトン2所有中(よって処理中) ↓
ProcessC::バトンが空いた!←←←←←←←←←←←
「そこで、 ProcessC はさっそくバトン1を取得します」
バトン1:ProcessC 所有中
バトン2:ProcessB 所有中
ProcessA::何もしてません
ProcessB::バトン2所有中(よって処理中)
ProcessC::バトン1所有中(よって処理中)
「バトンを受け取ったので監視は終了、処理を開始するわけです」
『なるほどねー。つまりさ、バトンをパスしたってことでしょ?』
「そういうこと。 ProcessC っていうランナーは ProcessA と同じコースで
待機していて、 ProcessA からバトンを受け取ったので全力疾走開始。
ProcessA は代わりに走るのをやめたわけです」
『まさにリレーねー』
「実際のところ、ミューテックスもセマフォも〈バトンリレー〉をするため
の仕組みだから」
『そか、できて当然ってわけね』
「ただ、ミューテックスとセマフォって仕組みとか使い方が違うから」
『確かにこれは混乱するよね……セマフォのデフォルトのカウントとか』
「でもやりたいことは一緒。これをちゃんと理解できてればミューテックス
と同じように普通に使えると思うよ」
/*
Preview Next Story!
*/
『次回はタイマーね。あれ? でもタイマーって前に使わなかった?』
「タイマーって全部で3つくらいあるから」
『そのうちのひとつ?』
「そゆこと」
「というわけで次回」
< Version 14.10 タイマーを使おう! >
『につづく!』
「ちなみに一番使わないタイマー」
『え”』