CThreadManager




Javaでは、スレッド起動後、そのスレッドが終了したときに、自動的にそのスレッドオブジェクトがガーベジコレクタによって後始末されます。

list-1
    new Thread(new ConcreteRunnable).start();


スレッド生成後、あとは何も意識せずとも済むというのは、なかなか魅惑的です。

もし、こうなっていなければ、スレッド生成後、そのスレッドの終了を明示的に待たなければならなくなるからです。明示的に待つと言っても、

list-2
    while (!thread.isThreadEnd())
            ;


というようにbusy waitにしてしまっては、スレッドを待つために、マシンパワーをずいぶん消費してしまいます。

そこで、同期オブジェクト(Win32のEvent。yaneSDK3rdにはCEventという、これをwrapしたクラスがある)を用いて、そいつがシグナル状態になるのを待つというようにしなければならないのですが、そのことを明示的に書かないといけないことが、どこかダサいと思うのです。

実行しているすべてのスレッドが終了したら、プログラムが終了する。そうは出来ないのか?と思ったのです。

そこで、まずスレッドマネージャを用意しました。(CThreadManager)

このスレッドマネージャには、スレッドを保持する機構があって、かつ、すべてのスレッドの終了を待つための仕組みがあります。たとえば、

list-3
{
    CThreadManager tm;
    tm.RunThread(new ConcreteThread1);
    tm.RunThread(new ConcreteThread2);
    tm.WaitAllThreadEnded();
}


というようにすれば、生成した2つのスレッドが終了するまでWaitAllThreadEndedで待たされます。

このようにスレッドマネージャを用いても良いのですが、WinMainを抜けるときにそれを勝手にやってくれたら、いいのに、と思いました。

そこで、yaneSDK2ndのときにあった、CAppInitializerのデストラクタでは、こっそりこの
CThreadManager::WaitAllThreadEnded();
を呼び出して、待つことにしました。なかなか良いアイデアです。

list-4

//  言わずと知れたWinMain
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
    CAppInitializer init(hInstance,hPrevInstance,lpCmdLine,nCmdShow);

    //  必ず書いてね
    CSingleApp sapp;
    if (sapp.IsValid()) {
        CThreadManager::CreateThread(new CAppMainWindow);
    }
    //  ここでCAppInitializerがスコープアウトするのだが、このときに
    //  すべてのスレッドの終了を待つことになる
    return 0;
}


もし、複数のウィンドゥを作りたいのならば、

list-5
        CThreadManager::CreateThread(new CAppMainWindow);

と書いてある行を

list-6
        CThreadManager::CreateThread(new CAppMainWindow);
        CThreadManager::CreateThread(new CAppMainWindow);
        CThreadManager::CreateThread(new CAppMainWindow);

というように書けば、3つのウィンドゥが作成されます。

ウィンドゥクラス(CAppBase,CAppFrame)については、yaneSDK2ndのほうと、ほぼ同じなのでそちらをご覧いただくとして、スレッドの終了条件について書いておきます。

上記のようにして、複数のウィンドゥを生成したとき、普通は、親ウィンドゥというものが存在して、その親ウィンドゥが終了するときにすべてのウィンドゥが閉じ、アプリが終了する、というのが一般的なモデルです。ところが、このスレッドモデルではすべてのスレッドが対等なので、親ウィンドゥとか子ウィンドゥという概念自体がありません。デバッグ表示のためのウィンドゥ(CDebugWindow)ですら、メインウィンドゥと対等なのです。

そこで、メインウィンドゥは、終了するときに、

枠-1
    CThreadManager::StopAllThread();


を呼び出して終了させます。こうすることによって、他のウィンドゥすべてが閉じるというわけです。また、これを補助するために、CAppBase/CAppFrameのメンバなのですが、

枠-2
    SetMainApp(bool);


というのがあります。

枠-3
    SetMainApp(true);


とやっておけば、そのウィンドゥが閉じらたときに、自動的にすべてのスレッドを終了させることが出来ます。