#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のトップページをご覧ください。