#pragma twice

KAB-studio > プログラミング > #pragma twice > 275 Version 14.08 セマフォを使ってみる!

#pragma twice 275 Version 14.08 セマフォを使ってみる!

前のページへ 表紙・目次へ 次のページへ

 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 セマフォで排他処理 >
につづく!
というわけでプログラムでちゃんと証明してみます
なら騙されっぱなしでもいいかも
なんでー?
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。