古の技術でゲームプログラミング 第7回

 

スレッドの作り方

 

 

WindowsAPIにはスレッドを作る以下の関数があります。

 

・CreateThread

 

しかし、だまされてはいけない。

 

Cランタイムライブラリを利用するアプリケーションにおいては以下を使わなくてはいけません。

 

ヘッダファイル:process.h

uintptr_t _beginthread( // NATIVE CODE
   void( __cdecl *start_address )( void * ),
   unsigned stack_size,
   void *arglist
);

 

パラメータの説明

・start_address

 スレッド起動時に実行される関数を指定する。

 ゲーム用スレッドならここでメインループを回す。

 

・stack_size

 0にしたらいい感じで割り当ててくれるらしい?

 

・arglist

 start_addressの関数に渡すパラメータを設定できる。

 

 

_beginthreadには兄弟がいて_beginthreadexってやつもいる。

exのほうはスレッドの初期状態を指定できたりちょっと拡張されてる。

注意点としては_beginthreadと_beginthreadexでは終了時の手続きがちょっと違う。

 

_beginthreadのほうはstart_addressの関数を終了したらスレッドは破棄されるが、_beginthreadexのほうは終了時に_endthreadexを呼び出す必要があるらしい。

 

 

これは_beginthreadで作られたスレッドは関数終了時にスレッドのハンドルをクローズしてくれるが、_beginthreadexで作られたスレッドは関数終了してもハンドルがクローズされないためらしい。

なので_beginthreadexを使った場合は_endthreadexをしないといけない。

 

 

ってことでThreadクラスを作ってみたので一部コード抜粋して載せておこうと思います。

void Thread::EntryPoint(void* lpParameter)
{
    Thread* this_ptr = (Thread*)lpParameter;

    this_ptr->ThreadMain();

    return;
}

void Thread::Run()
{
    // 停止しているときだけ処理
    if (!m_State)
    {
        // ハンドル無効のときだけスレッド生成
        if (m_hThread == NULL)
        {
            m_hThread = (void*)_beginthread(Thread::EntryPoint, 0, this);

            // 生成と同時に勝手に実行される
        }
    }

    return;
}

void Thread::ThreadMain()
{
    m_State = true;

    // ユーザー独自コードの実行
    UserMain();


    m_State = false;
    m_hThread = NULL; // 関数終了により自動でハンドル解放されるのでここでNULLをセットする

    return;
}

[解説] 

一部を載せただけなのであんまり意味がないけど解説。

Runをよぶと_beginthreadをしてスレッドが動き出す。

 

start_addressにはメンバ関数はそのまま渡せないのでstaticのThread::EntryPointを指定してます。

で、パラメータとして自身のインスタンスのポインタを渡してます。

EntryPointの側ではパラメータで受け取ったインスタンスのポインタを介してThreadMainを実行する。

ThreadMainでは派生先のクラスで実装するUserMainを呼び出す構成になっている。

ゲームのメインループ用のスレッドのほかにも、ファイル入出力とかその他裏でやっておきたい処理のために使いまわせるように作ってあります。

 

 

おしまい。