#pragma twice

KAB-studio > プログラミング > #pragma twice > 283 Version 14.16 スレッドを待ってみる

#pragma twice 283 Version 14.16 スレッドを待ってみる

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

 Version 14.16
スレッドを待ってみる

「スレッドの使い方にはいくつかパターンがあります。今回はそのひとつ、
スレッドが終わるまで待つというのをしてみます」
『スレッドが終わるまで待つ……んじゃ、スレッドを使う意味がないよう
な……』
「それはそうなんだけどね、これから複雑な処理をするための準備ってこと
で」
『う、複雑なのがあるんだ……』
「ただ、普通に待つだけなら簡単にできるよ」

// 別スレッドで呼び出される関数。
void __cdecl ThreadFunction( void *p_p )
{
    TRACE( "スレッドです。\n" );
    TRACE( "10 秒止まります。\n" );
    Sleep( 10 * 1000 );
    TRACE( "10 秒止まりました。\n" );
    TRACE( "スレッド終了します。\n" );
}

// スレッドを作って呼び出して待つ関数。
void WaitThread()
{
    HANDLE hThread 
        = (HANDLE)_beginthread( ThreadFunction, 0, NULL );

    TRACE( "待機開始。\n" );
    DWORD dwResult = WaitForSingleObject( hThread, 100 * 1000 );

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

『あ! WaitForSingleObject() だ!』
「そう、同期オブジェクトを待つときに使った API です。詳しくは 
Version 14.05 ( No.272 ) を参照」
『スレッドも同じように待てるの?』
「そう。まず、 _beginthread() の戻り値は、ミューテックスやセマフォと
同じ【ハンドル】なんです」
『そういえばミューテックスとかもハンドル取得してそれ使ってたね』
「このハンドルも、同期オブジェクトと同じようにシグナルが変わります」

・スレッド実行中:シグナルオフ
・スレッド終了 :シグナルオン

『なるほど、スレッドのハンドルを待てば、スレッドが終わるまで待ってい
て、終わったら待機終了、ってわけね』
「そう、こんな感じに」

待機開始。
スレッドです。
10 秒止まります。
10 秒止まりました。
スレッド終了します。
スレッド 0x798 終了、終了コード 0 (0x0)。
待機終了。

「 WaitForSingleObject() は他の同期オブジェクトと同じようにスレッド
を待ちます。そして、スレッドは終了するとシグナルがオンになります」
『なんかホントに同期オブジェクトみたい……あ、そういえば』

スレッド 0x1E0 終了、終了コード 0 (0x0)。

『って?』
「これはスレッド終了時のメッセージ。これはスレッドが終了したのを VC 
が検知して勝手に出力してるものだから、気にしなくていいかな」
『むー、気にしないってゆーのもアレね……』
「さて、この方法は普通には使いません」
『そなの?』
「最初のスレッドが、立ち上げたスレッドが終わるの待ってたら、ただの
シングルスレッドでしょ」
『そか、それじゃスレッド立ち上げる意味ないね』
「だから、するとしたら間にもうひとつスレッドを挟みます」
『へ?』

// 別スレッドで呼び出される関数。
void __cdecl ThreadFunction( void *p_p )
{
    TRACE( "スレッドです。\n" );
    TRACE( "10 秒止まります。\n" );
    Sleep( 10 * 1000 );
    TRACE( "10 秒止まりました。\n" );
    TRACE( "スレッド終了します。\n" );
}

// スレッドを作って呼び出して待つ関数。
// ただし、この関数も別スレッドとして呼び出されます。
void __cdecl WaitThread( void *p_p )
{
    HANDLE hThread 
        = (HANDLE)_beginthread( ThreadFunction, 0, NULL );

    TRACE( "待機開始。\n" );
    DWORD dwResult = WaitForSingleObject( hThread, 100 * 1000 );

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

// WaitThread() を別スレッドとして呼び出す関数。
// この関数をボタンのイベントハンドラとかで呼んでください。
void CallThread()
{
    TRACE( "WaitThread() 呼び出し。\n" );
    _beginthread( WaitThread, 0, NULL );
    TRACE( "WaitThread() 呼び出し完了。\n" );
}

『関数がみっつ……』
「呼び出す順番はこう」

CallThread() :メインスレッド
     ↓
WaitThread() :サブスレッド1
     ↓
ThreadFunction() :サブスレッド2

『間の WaitThread() も別スレッドで呼ぶんだ……』
「これをダイアログアプリで試してみると、ダイアログが自由に動かせるで
しょ」
『あ、ホントだ! さっきまで固まってたのに』
「これまでの例のように、メインスレッドでスレッドを作ってそれを待って
いると、メインスレッドが止まったままになっちゃうでしょ」
『それが、もうひとつスレッド作れば、大丈夫になる……』
「もう少し実用的な例で考えてみようか」

・サブスレッド2の処理を3つ平行に行う。
・3つの処理が完了したらサブスレッド1で残処理をする。

「という例を考えてみます」
『つまりサブスレッド23つが終わったらサブスレッド1でなんかす
る……』
「こういう場合、 WaitForMultipleObjects() っていう関数を使います」

// ThreadFunction() は同じ。

// スレッドを作って呼び出して待つ関数。
// ただし、この関数も別スレッドとして呼び出されます。
void __cdecl WaitThread( void *p_p )
{
    // 3つのハンドル。
    HANDLE hThreadAry[3];
    hThreadAry[0]
        = (HANDLE)_beginthread( ThreadFunction, 0, NULL );

    hThreadAry[1]
        = (HANDLE)_beginthread( ThreadFunction, 0, NULL );

    hThreadAry[2]
        = (HANDLE)_beginthread( ThreadFunction, 0, NULL );

    TRACE( "待機開始。\n" );
    DWORD dwResult
        = WaitForMultipleObjects( 3, hThreadAry, TRUE, 100 * 1000 );

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

// CallThread() は同じ。

『 ThreadFunction() を3回呼んで……配列に入れてる?』
「そう、 HANDLE の配列に入れて、それを WaitForMultipleObjects() とい
う API に渡します」
『 WaitForSingleObject() の複数版、ってこと?』
「そういうこと。 WaitForMultipleObjects() の第1引数はハンドルの数、
つまり第2引数の要素数。第2引数は監視対象のハンドルの配列」
『このハンドルを待機するわけね』
「第3引数は、全部待つか、ひとつだけ待つか。 TRUE だと、すべての
ハンドルがシグナルオンになるまで待ちます」
『 FALSE だと?』
「 FALSE だとひとつだけでもシグナルオンになったら待機終了」
『へー』
「実際の流れはこう」

WaitThread() 呼び出し。
WaitThread() 呼び出し完了。
待機開始。
スレッドです。
10 秒止まります。
スレッドです。
10 秒止まります。
スレッドです。
10 秒止まります。
10 秒止まりました。
スレッド終了します。
スレッド 0x7C8 終了、終了コード 0 (0x0)。
10 秒止まりました。
スレッド終了します。
スレッド 0x3A8 終了、終了コード 0 (0x0)。
10 秒止まりました。
スレッド終了します。
スレッド 0x37C 終了、終了コード 0 (0x0)。
待機終了。
スレッド 0x658 終了、終了コード 0 (0x0)。

『ホントだ、サブスレッド2が3つとも終わってからサブスレッド2が待機
終了してる』
「こういう使い方もできるってことで」

/*
    Preview Next Story!
*/
『でもなんかいまいち使い道わからない』
「というわけで次回からさらに具体的な使い方を見ていきます」
『そっちから教えてもらいたかったなー』
「スレッドだとそれは危険だから、基礎からこつこつと」
『えー?』
「というわけで次回」
< Version 14.17 スレッドで変数を共有する >
『につづく!』
「いや、本当にスレッドは危険なんだよね……」
『何泣いてんの……』
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。