#pragma twice

KAB-studio > プログラミング > #pragma twice > 274 Version 14.07 デッドロック!

#pragma twice 274 Version 14.07 デッドロック!

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

 Version 14.07
デッドロック!

「今回は、同期処理において最も注意しなければならない【デッドロック】
について説明します」
『でっどろっく?』
「英語で deadlock 、どうしようもない完全に固まった状態のことを表しま
す」
『なんか難しそう』
「ううん、全然難しくないよ。たとえば……」

・お店A
・お店B

「があるとします」
『いきなりお店?』
「例だから……このお店には、それぞれ次のようなルールがあります」

・お店A:お店Bが安くしたらうちも安くする。
・お店B:お店Aが安くしたらうちも安くする。

『なにこれ、いつまでたっても安くなんないじゃん』
「それがデッドロック」
『???』
「これをミューテックスに置き換えてみます」

・プロセスA
・プロセスB

「がある場合に、次のようなプログラムが書かれていたとします」

・プロセスA:プロセスBのミューテックスBが解放されたら
       ミューテックスAを解放する。
・プロセスB:プロセスAのミューテックスAが解放されたら
       ミューテックスBを解放する。

『???』
「図にするとこう」

プロセスA                            プロセスB
ミューテックスAの作成及び所有。
                                      ミューテックスAを取得。
                                      ミューテックスBの作成及び所有。
ミューテックスBを取得。
ミューテックスAを所有中←←←←←←←ミューテックスAを監視開始。
ミューテックスAを所有中              (監視中)
ミューテックスBを監視開始→→→→→→ミューテックスBを所有中
(監視中)                            (監視中)
(監視中)                            (監視中)
...

『げ、両方とも監視中になっちゃった!』
「つまり、AとB両方がミューテックスを持っていて、両方ともシグナルオ
フの時にお互いに監視を始めちゃうと、ずーっと見張りっ放しになっちゃう
んです」
『それまずいじゃん!』
「だからまずいんだって。こういう状態が【デッドロック】』
『まさにどうしようもないね……』
「これを実際にプログラムで試してみます。まず、前回作った ProcessA と
ProcessB のサンプルを用意してください」
『はい』
「それを、こう修正してください」

【ProcessA】
namespace B1
{

HANDLE g_hMutexA = NULL;
HANDLE g_hMutexB = NULL;
const char *const MUTEX_NAME_A = "ProcessesMutexA";
const char *const MUTEX_NAME_B = "ProcessesMutexB";

void CreateA()
{
    g_hMutexA = CreateMutex( NULL, TRUE, MUTEX_NAME_A );
    TRACE( "%x\n", g_hMutexA );
}

void OpenB()
{
    g_hMutexB = OpenMutex( MUTEX_ALL_ACCESS, FALSE, MUTEX_NAME_B );
    TRACE( "%x\n", g_hMutexB );
}

void WaitB()
{
    TRACE( "待機開始。\n" );
    DWORD dwResult = WaitForSingleObject( g_hMutexB, 120000 );

    if( dwResult == WAIT_TIMEOUT )
    {
        TRACE( "タイムアウト。\n" );
    }
    else
    {
        TRACE( "待機終了。\n" );
    }
}

void ReleaseB()
{
    ReleaseMutex( g_hMutexB );
}

void CloseA()
{
    CloseHandle( g_hMutexA );
}

}


【ProcessB】
namespace B1
{

HANDLE g_hMutexA = NULL;
HANDLE g_hMutexB = NULL;
const char *const MUTEX_NAME_A = "ProcessesMutexA";
const char *const MUTEX_NAME_B = "ProcessesMutexB";

void CreateB()
{
    g_hMutexB = CreateMutex( NULL, TRUE, MUTEX_NAME_B );
    TRACE( "%x\n", g_hMutexB );
}

void OpenA()
{
    g_hMutexA = OpenMutex( MUTEX_ALL_ACCESS, FALSE, MUTEX_NAME_A );
    TRACE( "%x\n", g_hMutexA );
}

void WaitA()
{
    TRACE( "待機開始。\n" );
    DWORD dwResult = WaitForSingleObject( g_hMutexA, 120000 );

    if( dwResult == WAIT_TIMEOUT )
    {
        TRACE( "タイムアウト。\n" );
    }
    else
    {
        TRACE( "待機終了。\n" );
    }
}

void ReleaseA()
{
    ReleaseMutex( g_hMutexA );
}

void CloseB()
{
    CloseHandle( g_hMutexB );
}

}

『長!』
「前回と同じように、各関数をボタンのハンドラから呼ぶようにしてくださ
い」
『なんか違いが微妙すぎていまいちわかんないんだけど』
「簡単に言えば

     | 作成            | 待機
ProcessA | ProcessesMutexA | ProcessesMutexB
ProcessB | ProcessesMutexB | ProcessesMutexA

「という感じに、相手が作ったミューテックスを待機の相手にする、ってい
う形になります」
『そか、そうすればさっきのデッドロックになる!』
「というわけで試してみましょう。まず ProcessA::CreateA() を呼んで 
ProcessesMutexA を作成」
『ほい』
「次に ProcessB::CreateB() を呼んで ProcessesMutexB を作成」
『ほい』
「次に ProcessB::OpenA() を呼んで ProcessesMutexA のハンドルを取得、
さらに ProcessB::WaitA() を呼んで待機開始」
『でも ProcessA に所有権があるから待ちっぱなし、ね』
「で、この状態の時に ProcessA::OpenB() を呼んで ProcessesMutexB の
ハンドルを取得、そして ProcessA::WaitB() を呼ぶと」
『 ProcessB に所有権があるままだから……お、固まった』
「というわけでデッドロックの完成。 ProcessA ProcessB 両方とも固まっ
たままになります」
『あちゃー……』
「ちなみにこのプログラムはタイムアウト時間を2分に設定してあるから」
『 WaitForSingleObject() の第2引数ね。お、2分待ったら復活した』
「というわけで、デッドロックは気を付けないと簡単に発生します」
『回避方法ってどうすればいいの?』
「それについてはあとでちゃんと説明するけど、とりあえず一番簡単な方法
は〈同期オブジェクトはひとつしか作らない〉ことかな」
『そっか、そうすればお互いにお見合いすることないもんね』
「ほとんどの場合ひとつで十分だし、複数必要でもセマフォを使えばなんと
かなることも多いから」
『セマフォ?』
「というわけで次回に続く!」

/*
    Preview Next Story!
*/
『せまふぉ、ってなんか言いにくい』
「慣れが必要な単語のひとつかも」
『みゅーてっくすもそうだし』
「でも使えるようになるととたんに気に入るから」
『確かに……セマフォ、セマフォ、セマフォ、かっこいいかも?』
「というわけで次回」
< Version 14.08 セマフォを使ってみる! >
『につづく!』
「ちなみにセマフォは【信号機】って意味」
『うわかっこわる!』
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。