標准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以方便使用,通常難度不大,代碼也不會太長。這是一個典型的例子。