#pragma twice

KAB-studio > プログラミング > #pragma twice > 291 Version 14.24 メッセージループとスレッド

#pragma twice 291 Version 14.24 メッセージループとスレッド

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

 Version 14.24
メッセージループとスレッド

今回は、前回作ったウィンドウプログラムを元に、スレッドについて見て
いきます
それなんだけど、それってどう結びつくのかよく分かんない
なぜ結びつくかというと、ウィンドウ、っていうがスレッドと関係してた
りしてなかったりするから
??? してたりしてなかったり?
そう。そういった部分をはっきりするためにも、メッセージループを実際
にプログラムで作ってみて、それを試していきます。まず、さっきから話題
にしているメッセージループについて。これはこの while を指します

    // メッセージループです。
    while( 1 )
    {
        // メッセージをキューから取り出します。
        bRes = GetMessage( &stMsg, NULL, 0, 0 );
        if  ( 
                ( bRes == 0 )
            || 
                ( bRes == -1 )
            )
        {
            // 終了するのでループから抜けます。
            break;
        }

        // メッセージを変換します。
        TranslateMessage( &stMsg );
        // ウィンドウプロシージャに送ります。
        DispatchMessage( &stMsg );
    }

これがメッセージループなんだよね、半永久的に回る
このメッセージループでは、まずメッセージキューからメッセージを取り
出します

        // メッセージをキューから取り出します。
        bRes = GetMessage( &stMsg, NULL, 0, 0 );

メッセージは、たとえばウィンドウがアクティブになったり、キー入力さ
れたり、そういう【イベント】の情報が入ったものを指します。これが第1
引数の stMsg 構造体に格納されます
 WM_ なんたらっていうのがメッセージなんだよね
そうそう。そういったメッセージは、 DispatchMessage() でウィンドウ
プロシージャに送られます

        // ウィンドウプロシージャに送ります。
        DispatchMessage( &stMsg );

これが呼ばれると WndProc() の方にメッセージが送られて、そっちで
メッセージを処理するんだよね
そういうこと。そしてまた始めに戻ります
メッセージキューからメッセージ取り出すとこね
さて、ここで次の場合を考えてみます

・メッセージがない場合、 GetMessage() はどうなる?

……どうなるんだろう……メッセージキューにメッセージが貯まってな
いってことだよね。メッセージがないからメッセージが来るまで待つのか
な、それともすぐ返ってきて次のループに行くのかな……
というわけで

        // メッセージをキューから取り出します。
        bRes = GetMessage( &stMsg, NULL, 0, 0 );
        OutputDebugString( "メッセージ来た!\n" );

ってして、メッセージが来ない場合にどう出力されるのか見てみて
ほい。……あ、なんもしないでほっぽっとくと、何も出ない、ってことは 
GetMessage() から返ってきてないんだ
そういうこと。 GetMessage() は、メッセージがない場合にはメッセージ
が来るまでずっと待ち続けます
おお、けなげ
もうひとつ重要なのは、待ってる間、 CPU パワーは全然使わないってこ


もしさっき火美ちゃんが言ってたように、 GetMessage() がすぐ返ってき
て、ループが繰り返されてたら、ずーっとまわりっぱなしでしょ
メッセージが来ない間も while のなかをぐるぐると……それってずっと
動きっぱなし、それが CPU パワー使っちゃうってこと?
そういうこと。でも実際には、 GetMessage() はメッセージが来るまで
ずっと待ち続けて、しかも CPU は使用しないんです
何もしてない時はこうやって CPU 使わないわけだ……
さて、もうひとつ、こんな場合を考えてみます

・メッセージがいっぱいある場合、 GetMessage() はどうなる?

ん? そんなのわかりきってない?
じゃあどうなると思う?
え、えーっと……メッセージキューにメッセージがいっぱい入ってても、
まずひとつ取り出して、それをウィンドウプロシージャに渡して処理して、
んで次の周で2番目のを取り出して、それをウィンドウプロシージャに渡し
て……って続くんじゃない?
うん、そうなります。プログラムで追っていくと

        // メッセージをキューから取り出します。
        // 【メッセージ1】【メッセージ2】...(←キューの中身)
        bRes = GetMessage( &stMsg, NULL, 0, 0 );
        // 【メッセージ2】...(←キューの中身)
        // stMsg には【メッセージ1】が入ってます。
// 略
        // ウィンドウプロシージャに送ります。
        // 【メッセージ1】を送ります。
        DispatchMessage( &stMsg );

これが1周目。2周目には【メッセージ2】が同じように処理されます
なんだ、あってんじゃん
では、【メッセージ1】の処理内容がすごく重い場合、どうなるでしょ

え、重い場合??
そう、たとえば【メッセージ1】が WM_ACTIVATE だった場合、その際の
再描画ですごく大きな画像を出力しなきゃいけないときとか
それだと、ウィンドウプロシージャで止まってることになるよね……む、
そういうときって DispatchMessage() って返ってくるのかな、それとも
ウィンドウプロシージャでの処理が終わるまで返ってこないのかな
返ってきません。ウィンドウプロシージャでの処理が終わらないとね
ってことは、その重い処理が終わらない限りループは2周目に行かない、
メッセージ2は処理されない……
ってことになります。これがいわゆる〈固まった〉状態
あ、なるほど、ウィンドウがうんともすんとも言わなくなっちゃうのは、
こうやってメッセージひとつ止まっちゃうと、次のメッセージとかも全部止
まっちゃうからなんだ
そうなんです。さて、今火美ちゃんが悩んだところに、実はすごく大きな
ことが含まれているんです
え、そうなの?
今、火美ちゃんは〈DispatchMessage() って返ってくるのかな〉って言っ
たけど、もし返ってくるとしたら、メッセージループとウィンドウプロシー
ジャはどうなる?
どうなるって、もし DispatchMessage() が呼ぶとすぐ返ってくるとする
と、メッセージループはすぐ次のループになって、ウィンドウプロシージャ
では同時に送られてきたメッセージを処理する……あ”!!
そう、それって
マルチスレッド!!
そういうこと。火美ちゃんが言った状態は、マルチスレッドを指してるん
です
はー、そういうことなんだ
そして、火美ちゃんが言ったような状態に自動的にはなりません。つま
り、勝手にマルチスレッドになったりはしないんです
あの関数とか API 呼ばない限り、スレッドはひとつだけってこと?
そういうこと。だから

        // ウィンドウプロシージャに送ります。
        DispatchMessage( &stMsg );

でメッセージを送ると、スレッドの〈進行〉はウィンドウプロシージャに
飛びます

// ウィンドウプロシージャ。
LRESULT CALLBACK WndProc
    ( HWND p_hWnd
    , UINT p_uiMessage
    , WPARAM p_wParam
    , LPARAM p_lParam
    )
{
    // この中でスレッドが進行します。
}

関数を呼び出したりした時に、メインのスレッドとは別のスレッドが勝手
に処理してくれる、なんてことはないんです。メッセージループを処理して
いるスレッドも、ウィンドウプロシージャを処理しているスレッドも、同じ
ひとつのスレッドなんです
同じスレッド……
ここがとても重要なんです。メッセージループやウィンドウプロシージャ
が関わってくると、プログラムの流れ、スレッドの進行が無視されがちなん
だけど、実際にはシンプルな話。一見マルチスレッドしてそうでも、実はし
てないんです
確かに、ウィンドウの操作とか、ぱっと見マルチスレッドっぽい感じする
もん。でも違うってことね。そうでなきゃ固まらない、と
さらに話を進めると、メインのスレッドは最初から最後までちゃんと継な
がってるんです
? どゆこと?
プログラムはどこから始まる?
 WinMain() 

// 最初に呼ばれる関数です。
int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    // プログラムはここから始まる。
// 略
    return MessageLoop();
}

 WinMain() から始まったプログラムの流れ、つまりメインスレッドの
進行は、メッセージループに入ります

    // メッセージループです。
    while( 1 )
    {
        // メッセージをキューから取り出します。
        bRes = GetMessage( &stMsg, NULL, 0, 0 );
// 略
        // ウィンドウプロシージャに送ります。
        DispatchMessage( &stMsg );
    }

この中でもメインスレッドがループしてます。 GetMessage() にメッセー
ジがなければスレッドは停止してるし、 DispatchMessage() の先ではウィ
ンドウプロシージャまでメインスレッドが行って処理します。そして、終了
する時にはメッセージループから抜けて、 WinMain() からも抜けて、アプ
リケーションが終了します
……全部継ながってるってこと?
というわけで次回に続く!

/*
    Preview Next Story!
*/
固まるってこういうことだったのね……
まぁアプリ毎に違うけど、基本的にはこういうこと
固まるアプリって最低よねー
だから、自分が作るアプリはそういうことがないようにしないと
……それはそれでめんどくさい
というわけで次回
< Version 14.25 2種類のマルチスレッド >
につづく!
マルチスレッドがちゃんとわかれば固まらなくなるから
だからめんどくさいって
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。