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