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で書き換えてみたいと思います。
サンプル
- thread.zip
- VC++2008EE用