最初發表在QQ空間:標準C++中實現線程類
標準C++和標準庫中沒有對線程的封裝,程式員們不得不使用OS提供的API來處理線程,OS層級的API通常基於C,能用,但並不方便。最近看到論壇上有人問,順便和同事討論這個問題,如何使用標準C++封裝線程的操作,目的就是simple and easy to use。想想自己似乎多年前(已經結蜘蛛網了)寫過這方面的代碼,找了找,還真找到了,是Windows平台的,整理一下,與大家分享。
// 抽象類別,作為線程類的基類,定義了幾個介面// abstract class to provide interface.// class GeneralThread{public: virtual ~GeneralThread() {}public: // create thread and run with specified priority virtual void Run( long priority = THREAD_PRIORITY_BELOW_NORMAL ) = 0; // wait thread running till timeout virtual unsigned long Join( unsigned long ms = INFINITE ) = 0; virtual unsigned long GetExitCode() const = 0; // end thread ingore thread status. virtual void End() {}};typedef GeneralThread GThread;typedef GThread * CThreadPtr; // 一個子類,實現了基類的介面,並且定義了一個新的介面來運行真正的線程函數// 因此,可以從這個類繼續派生新的子類,實現自訂的線程函數。// a derived calss from GeneralThread// class SomeThread : public GeneralThread{public: SomeThread() : m_hThread(0) { } ~SomeThread() { //Join( INFINITE ); if( m_hThread ) { CloseHandle( m_hThread ); m_hThread = NULL; } }public: // new interface to implement thread actions virtual unsigned long ThreadProc() = 0;public: virtual void Run( long priority ) { m_hThread = CreateThread( NULL, 0, &SomeThread::ThreadProc, this, CREATE_SUSPENDED, NULL ); if( m_hThread ) { SetThreadPriority( m_hThread, priority ); ResumeThread( m_hThread ); } else { DWORD dw = GetLastError(); UNREFERENCED_PARAMETER( dw ); } } virtual unsigned long Join( unsigned long ms ) { unsigned long ul = WAIT_OBJECT_0; if( m_hThread ) { ul = WaitForSingleObject( m_hThread, ms ); switch( ul ) { case WAIT_OBJECT_0: //GetExitCodeThread( m_hThread, &m_exitCode ); break; case WAIT_TIMEOUT: break; case WAIT_FAILED: ul = ul; break; } } return ul; } virtual unsigned long GetExitCode() const { DWORD exitCode = 0; GetExitCodeThread( m_hThread, &exitCode ); return exitCode; } virtual void End() { TerminateThread( m_hThread, 0xabcd ); }private: static unsigned long WINAPI ThreadProc( LPVOID lpParameter ) { SomeThread *p = static_cast< SomeThread * >( lpParameter ); return p->ThreadProc(); }private: HANDLE m_hThread;}; // 雖然可以從SomeThread 派生子類,但是如果有多個線程,並且每個線程的線程函數不一樣的話,// 那麼需要實現多個子類,並不是很方便。考慮到標準C++推薦使用模板和函數對象,因此派生了一個// 子類,重新實現了父類中的虛函數,轉寄成對函數對象的訪問。//// if you want to implement your thread, you have to derive a class from SomeThread and also implement your thread procedure.// sometimes you will feel boring.// so here we implement a template class to simplify usage.// thus you don't need to code your derived class, instead just provide your function object.// template< typename F >class ConcreteThread : public SomeThread{public: ConcreteThread( const F &f ) : m_f(f) { }private: unsigned long ThreadProc() { return m_f(); }private: F m_f;};template< typename F >CThreadPtr MakeThread( F &f ){ return new ConcreteThread< F >( f );} // 這個類提供了另一種形式的封裝。// this class is just for simple usage in stack scope.// class Thread{public: template< typename F > Thread( F &f ) : m_pThread( MakeThread(f) ) { m_pThread->Run(); } ~Thread() { delete m_pThread; }public: unsigned long Join() { return m_pThread->Join(); } unsigned long ExitCode() { return m_pThread->GetExitCode(); }private: CThreadPtr m_pThread;};
代碼不長,而且加了些注釋,不難理解。下面是測試用的代碼
int sum( int end ){ int sum = 0; for( int i = 0 ; i < end ; i++ ) { sum += i; } return sum;}void TestThread(){ // test Thread class Thread t( std::bind( sum, 10000 ) ), t2( std::bind( sum, 20000 ) ); t.Join(); t2.Join(); std::cout << "sum1 = " << t.ExitCode() << "; sum2 = " << t2.ExitCode() << std::endl; // test ConcreteThread CThreadPtr p = MakeThread( std::bind( sum, 50000 ) ); p->Run(); p->Join(); std::cout << "sum3 = " << p->GetExitCode() << std::endl; //delete p; std::auto_ptr< GeneralThread > p2( MakeThread( std::bind( sum, 50001 ) ) ); p->Run(); p->Join(); std::cout <<"sum4 = " << p->GetExitCode() << std::endl;}
測試代碼很簡短,使用了標準C++的std::bind把sum函數封裝成函數對象,然後在單獨的線程中運行。
一般而言,使用C++封裝系統API以方便使用,通常難度不大,代碼也不會太長。這是一個典型的例子。