C++提供了封装对象的方法,但是没有提供多线程的支持,如果需要产生多个同时自主运行的对象(live-object),就需要定义我们自己的多线程类。MSDN中的《Using Multithreading and C++ to Generate Live Objects》对这个有趣的技术进行了详细的介绍。
废话少说,先来看一下产生一个支持多线程类的结构,下面以animate_object为例,先看一下animate_object的构造函数:
animated_object::animated_object(LPSTR lpImageName, short sType)
{
< Initialize some member variables here. >
hThread = CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE)ObjectThreadRoutine,
this,
0,
(LPDWORD)&this->dwIDThread);
iStatus = (hThread != (HANDLE)NULL);
};
可以看到,在构造函数中产生了一个线程,hThread, 用于表示该对象所联系的对象,并且用iStatus表示是否成功产生了该线程,其中hThread作为private成员,其并不为外界所知,外部环境获得该对象的信息的唯一方式就是通过iStatus,这样实现了数据的封装,而且iStatus还可以用于表示所有初始化工作的成功或者失败
下面是线程函数。由于线程函数不能够是非静态成员函数,所以需要使用一个全局函数
long WINAPI ObjectThreadRoutine(animated_object *fpObj)
{
return (fpObj->MoveAndDraw());
}
该全局函数实际上是一个stub function, 在函数内部调用了animated_object 对象的成员函数MoveAndDraw,为了让该全局函数可以使用animated_object的私有函数,需要将该全局函数声明为animated_object的友员函数。
下面是析构函数
animated_object::~animated_object(void)
{
if (iStatus)
{
WaitForSingleObject(hThread,INFINITE);
CloseHandle(hThread);
};
DeleteObject(hImage);
MessageBeep(-1);
};
当对象析构的时候,在析构函数内部等待线程结束,然后关闭线程,注意只有当初始化成功(iStatus非0)的时候,才会调用这些cleanup的工作,现在唯一的问题就是:如果该线程自己调用析构函数,就会产生死锁(A调用B,B返回的条件是A已经结束),这里可以采用postMessage的方式,特别要注意的是如果使用sendMessage,仍然会产生死锁(可以用共享变量的方法避免死锁)关闭的方式,在线程函数中使用postMessage将关闭自己的消息发送给外部控制线程,由外部控制线程关闭线程,这样在调用析构函数的时候就不会产生死锁的问题了。
同步问题:当产生多个该类的实例的时候,如果需要访问共享变量,需要使用互斥同步的方法,下面是animated_object的核心部分
long animated_object::MoveAndDraw(void)
{ int iTempX, iTempY;
HDC hDC;
while(!bFinished)
{
iTempX = iX+iXVelocity;
iTempY = iY+iYVelocity;
/* The next four IF statements check whether a wall has been hit after a move.
If yes, the position is reset to the wall, the direction reversed,
and a random value added. */
< Wall-collison detection code follows here... */
EnterCriticalSection(&csSerializeDraw);
hDC=GetDC(the_pond->hWndApplication);
< Erase the old image from the screen: >
BitBlt(hDC,iX,iY,iImageWidth,iImageHeight,NULL,0,0,WHITENESS);
iX = iTempX;
iY = iTempY;
iCalories-=MOVE_DEDUCTION;
/* Do three things here to determine whether one has died:
*/
/* (a) Do additional per-move work, depending on the object type. */
/* (b) Determine whether we have enough fuel left to survive. */
/* (c) Ask the pond whether a collision has occurred. */
if (!ObjectSpecificThreadRoutine(hDC) ||
// LOOK HERE!
iCalories < 0 ||
!the_pond->NewCoordinates(iHandle,iX,iY))
bFinished=TRUE;
else
/* Redraw the object. */
< Redraw code is in here.>
ReleaseDC(the_pond->hWndApplication,hDC);
LeaveCriticalSection(&csSerializeDraw);
/* Delay the object before moving on. */
Sleep(DELAY);
};
// end of while loop. Going past here means we are dead...
/* Inform the window that the thread has terminated, so it can clean up the object. */
PostMessage(the_pond->hWndApplication,WM_OBJECT_DIED,(WPARAM)iHandle,NULL);
return(TRUE);
};
结论:通过使用C++的封装特性和多线程,可以很好的模拟live object,其他复杂的应用也可以通过这种方式进行扩展,可以模拟bolzman机这种需要同步运行的程序,并且作为并行程序在多CPU环境下“真正”的并行。