threadクラスの実装。その1

XAudio2のイベント通知のためにthreadクラスが必要になったので、テンプレートを使って実装してみたいと思います。ただし、pthreadは使ったことがないので、windowsのみの実装になります。

使い方

コンストラクタに戻り値void、引数voidの関数または関数オブジェクトを渡して、スレッドを開始します。

void hoge(){ std::cout << "hoge" << std::endl; }

struct piyo
{
    void operator()(){ std::cout << "piyo" << std::endl; }
};

int main()
{
    thread t1( &hoge );
    piyo p;
    thread t2( p );

    t1.join();
    t2.join();
    return 0;
}

実装

任意の型の関数、関数オブジェクトを保持するために、いわゆるType Erasure(型消去)を使います。

threadが実行する関数を保持するためのクラス

windowsに依存する部分をまとめました。

#include <windows.h>
#include <process.h>
typedef HANDLE handle_t;

class thread_data_base
{
private:
    static unsigned __stdcall thread_start_function( void* param )
    {
        thread_data_base* data =
            reinterpret_cast< thread_data_base* >( param );
        data->run();
        return 0;
    }

public:
    thread_data_base() : handle( 0 ){}
    virtual ~thread_data_base(){ close(); }

    virtual void run() = 0;

public:
    //
    void close()
    {
        if( handle != 0 )
        {
            ::CloseHandle( handle );
            handle = 0;
        }
    }
    //スレッドを開始する。
    void start()
    {
        if( handle == 0 )
        {
            handle = ( HANDLE )::_beginthreadex( 0, 0,
                &thread_start_function, this, CREATE_SUSPENDED,
                0 );
            if( handle == 0 )
                throw thread_resource_error();

            ::ResumeThread( handle );
        }
    }
    //スレッドの終了を待機する。
    unsigned long wait( unsigned long timeout = INFINITE )
    {
        return ::WaitForSingleObject( handle, timeout );
    }


private:
    handle_t handle;
};
thread用例外クラス
class thread_exception : public std::exception
{
public:
    thread_exception() : std::exception( "thread exception" ){}
};
class thread_resource_error : public thread_exception{};
threadクラス
template < typename Func >
class thread_data
    : public thread_data_base
{
public:
    thread_data( Func f ) : f( f ){}
public:
    //関数または関数オブジェクトの実行
    void run(){ f(); }
private:
    Func f;         ///<    関数または関数オブジェクトを保持する

    //non-copyable
private:
    thread_data( const thread_data& );
    void operator=( const thread_data& );
};

class thread
{
public:
    template < typename Func >
    explicit thread( Func f )
        : data( new thread_data< Func >( f ) )
    {
        data->start();
    }

    virtual ~thread()
    {
        delete data;
    }

public:
    //スレッドの終了を待機する。
    void join()
    {
        data->wait();
    }

private:
    thread_data_base* data;

    //non-copyable
private:
    thread( const thread& );
    thread& operator=( const thread& );
};

次回は、thread_data_base*をboostのようにintrusive_ptrで書き換えてみたいと思います。

サンプル