线程.docx
《线程.docx》由会员分享,可在线阅读,更多相关《线程.docx(25页珍藏版)》请在冰豆网上搜索。
线程
1、创建和终止线程
在MFC程序中创建一个线程,宜调用AfxBeginThread函数。
该函数因参数不同而具有两种重载版本,分别对应工作者线程和用户接口(UI)线程。
工作者线程
CWinThread*AfxBeginThread(
AFX_THREADPROCpfnThreadProc,//控制函数
LPVOIDpParam,//传递给控制函数的参数
intnPriority=THREAD_PRIORITY_NORMAL,//线程的优先级
UINTnStackSize=0,//线程的堆栈大小
DWORDdwCreateFlags=0,//线程的创建标志
LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL//线程的安全属性
);
工作者线程编程较为简单,只需编写线程控制函数和启动线程即可。
下面的代码给出了定义一个控制函数和启动它的过程:
//线程控制函数
UINTMfcThreadProc(LPVOIDlpParam)
{
CExampleClass*lpObject=(CExampleClass*)lpParam;
if(lpObject==NULL||!
lpObject->IsKindof(RUNTIME_CLASS(CExampleClass)))
return-1;//输入参数非法
//线程成功启动
while
(1)
return0;
}
//在MFC程序中启动线程
AfxBeginThread(MfcThreadProc,lpObject);
UI线程
创建用户界面线程时,必须首先从CWinThread派生类,并使用DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏声明此类。
下面给出了CWinThread类的原型(添加了关于其重要函数功能和是否需要被继承类重载的注释):
classCWinThread:
publicCCmdTarget
{
DECLARE_DYNAMIC(CWinThread)
public:
//Constructors
CWinThread();
BOOLCreateThread(DWORDdwCreateFlags=0,UINTnStackSize=0,
LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL);
//Attributes
CWnd*m_pMainWnd;//mainwindow(usuallysameAfxGetApp()->m_pMainWnd)
CWnd*m_pActiveWnd;//activemainwindow(maynotbem_pMainWnd)
BOOLm_bAutoDelete;//enables'deletethis'afterthreadtermination
//onlyvalidwhilerunning
HANDLEm_hThread;//thisthread'sHANDLE
operatorHANDLE()const;
DWORDm_nThreadID;//thisthread'sID
intGetThreadPriority();
BOOLSetThreadPriority(intnPriority);
//Operations
DWORDSuspendThread();
DWORDResumeThread();
BOOLPostThreadMessage(UINTmessage,WPARAMwParam,LPARAMlParam);
//Overridables
//执行线程实例初始化,必须重写
virtualBOOLInitInstance();
//runningandidleprocessing
//控制线程的函数,包含消息泵,一般不重写
virtualintRun();
//消息调度到TranslateMessage和DispatchMessage之前对其进行筛选,
//通常不重写
virtualBOOLPreTranslateMessage(MSG*pMsg);
virtualBOOLPumpMessage();//lowlevelmessagepump
//执行线程特定的闲置时间处理,通常不重写
virtualBOOLOnIdle(LONGlCount);//returnTRUEifmoreidleprocessing
virtualBOOLIsIdleMessage(MSG*pMsg);//checksforspecialmessages
//线程终止时执行清除,通常需要重写
virtualintExitInstance();//defaultwill'deletethis'
//截获由线程的消息和命令处理程序引发的未处理异常,通常不重写
virtualLRESULTProcessWndProcException(CException*e,constMSG*pMsg);
//Advanced:
handlingmessagessenttomessagefilterhook
virtualBOOLProcessMessageFilter(intcode,LPMSGlpMsg);
//Advanced:
virtualaccesstom_pMainWnd
virtualCWnd*GetMainWnd();
//Implementation
public:
virtual~CWinThread();
#ifdef_DEBUG
virtualvoidAssertValid()const;
virtualvoidDump(CDumpContext&dc)const;
intm_nDisablePumpCount;//Diagnostictraptodetectillegalre-entrancy
#endif
voidCommonConstruct();
virtualvoidDelete();
//'deletethis'onlyifm_bAutoDelete==TRUE
//messagepumpforRun
MSGm_msgCur;//currentmessage
public:
//constructorusedbyimplementationofAfxBeginThread
CWinThread(AFX_THREADPROCpfnThreadProc,LPVOIDpParam);
//validafterconstruction
LPVOIDm_pThreadParams;//genericparameterspassedtostartingfunction
AFX_THREADPROCm_pfnThreadProc;
//setafterOLEisinitialized
void(AFXAPI*m_lpfnOleTermOrFreeLib)(BOOL,BOOL);
COleMessageFilter*m_pMessageFilter;
protected:
CPointm_ptCursorLast;//lastmouseposition
UINTm_nMsgLast;//lastmousemessage
BOOLDispatchThreadMessageEx(MSG*msg);//helper
voidDispatchThreadMessage(MSG*msg);//obsolete
};
启动UI线程的AfxBeginThread函数的原型为:
CWinThread*AfxBeginThread(
//从CWinThread派生的类的RUNTIME_CLASS
CRuntimeClass*pThreadClass,
intnPriority=THREAD_PRIORITY_NORMAL,
UINTnStackSize=0,
DWORDdwCreateFlags=0,
LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL
);
我们可以方便地使用VC++6.0类向导定义一个继承自CWinThread的用户线程类。
下面给出产生我们自定义的CWinThread子类CMyUIThread的方法。
打开VC++6.0类向导,在如下窗口中选择BaseClass类为CWinThread,输入子类名为CMyUIThread,点击"OK"按钮后就产生了类CMyUIThread。
其源代码框架为:
/////////////////////////////////////////////////////////////////////////////
//CMyUIThreadthread
classCMyUIThread:
publicCWinThread
{
DECLARE_DYNCREATE(CMyUIThread)
protected:
CMyUIThread();//protectedconstructorusedbydynamiccreation
//Attributes
public:
//Operations
public:
//Overrides
//ClassWizardgeneratedvirtualfunctionoverrides
//{{AFX_VIRTUAL(CMyUIThread)
public:
virtualBOOLInitInstance();
virtualintExitInstance();
//}}AFX_VIRTUAL
//Implementation
protected:
virtual~CMyUIThread();
//Generatedmessagemapfunctions
//{{AFX_MSG(CMyUIThread)
//NOTE-theClassWizardwilladdandremovememberfunctionshere.
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/////////////////////////////////////////////////////////////////////////////
//CMyUIThread
IMPLEMENT_DYNCREATE(CMyUIThread,CWinThread)
CMyUIThread:
:
CMyUIThread()
{}
CMyUIThread:
:
~CMyUIThread()
{}
BOOLCMyUIThread:
:
InitInstance()
{
//TODO:
performandper-threadinitializationhere
returnTRUE;
}
intCMyUIThread:
:
ExitInstance()
{
//TODO:
performanyper-threadcleanuphere
returnCWinThread:
:
ExitInstance();
}
BEGIN_MESSAGE_MAP(CMyUIThread,CWinThread)
//{{AFX_MSG_MAP(CMyUIThread)
//NOTE-theClassWizardwilladdandremovemappingmacroshere.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
使用下列代码就可以启动这个UI线程:
CMyUIThread*pThread;
pThread=(CMyUIThread*)
AfxBeginThread(RUNTIME_CLASS(CMyUIThread));
另外,我们也可以不用AfxBeginThread创建线程,而是分如下两步完成:
(1)调用线程类的构造函数创建一个线程对象;
(2)调用CWinThread:
:
CreateThread函数来启动该线程。
在线程自身内调用AfxEndThread函数可以终止该线程:
voidAfxEndThread(
UINTnExitCode//theexitcodeofthethread
);
对于UI线程而言,如果消息队列中放入了WM_QUIT消息,将结束线程。
关于UI线程和工作者线程的分配,最好的做法是:
将所有与UI相关的操作放入主线程,其它的纯粹的运算工作交给独立的数个工作者线程。
候捷先生早些时间喜欢为MDI程序的每个窗口创建一个线程,他后来澄清了这个错误。
因为如果为MDI程序的每个窗口都单独创建一个线程,在窗口进行切换的时候,将进行线程的上下文切换!
2.线程间通信
MFC中定义了继承自CSyncObject类的CCriticalSection、CCEvent、CMutex、CSemaphore类封装和简化了WIN32API所提供的临界区、事件、互斥和信号量。
使用这些同步机制,必须包含"Afxmt.h"头文件。
下图给出了类的继承关系:
作为CSyncObject类的继承类,我们仅仅使用基类CSyncObject的接口函数就可以方便、统一的操作CCriticalSection、CCEvent、CMutex、CSemaphore类,下面是CSyncObject类的原型:
classCSyncObject:
publicCObject
{
DECLARE_DYNAMIC(CSyncObject)
//Constructor
public:
CSyncObject(LPCTSTRpstrName);
//Attributes
public:
operatorHANDLE()const;
HANDLEm_hObject;
//Operations
virtualBOOLLock(DWORDdwTimeout=INFINITE);
virtualBOOLUnlock()=0;
virtualBOOLUnlock(LONG/*lCount*/,LPLONG/*lpPrevCount="NULL"*/)
{returnTRUE;}
//Implementation
public:
virtual~CSyncObject();
#ifdef_DEBUG
CStringm_strName;
virtualvoidAssertValid()const;
virtualvoidDump(CDumpContext&dc)const;
#endif
friendclassCSingleLock;
friendclassCMultiLock;
};
CSyncObject类最主要的两个函数是Lock和Unlock,若我们直接使用CSyncObject类及其派生类,我们需要非常小心地在Lock之后调用Unlock。
MFC提供的另两个类CSingleLock(等待一个对象)和CMultiLock(等待多个对象)为我们编写应用程序提供了更灵活的机制,下面以实际来阐述CSingleLock的用法:
classCThreadSafeWnd
~CThreadSafeWnd(){}
voidSetWindow(CWnd*pwnd)
{
m_pCWnd=pwnd;
}
voidPaintBall(COLORREFcolor,CRect&rc);
private:
CWnd*m_pCWnd;
CCriticalSectionm_CSect;
};
voidCThreadSafeWnd:
:
PaintBall(COLORREFcolor,CRect&rc)
{
CSingleLockcsl(&m_CSect);
//缺省的Timeout是INFINITE,只有m_Csect被激活,csl.Lock()才能返回
//true,这里一直等待
if(csl.Lock())
;
{
//notnecessary
//AFX_MANAGE_STATE(AfxGetStaticModuleState());
CDC*pdc=m_pCWnd->GetDC();
CBrushbrush(color);
CBrush*oldbrush=pdc->SelectObject(&brush);
pdc->Ellipse(rc);
pdc->SelectObject(oldbrush);
GdiFlush();//don'twaittoupdatethedisplay
}
}
上述实例讲述了用CSingleLock对WindowsGDI相关对象进行保护的方法,下面再给出一个其他方面的例子:
intarray1[10],array2[10];
CMutexSectionsection;//创建一个CMutex类的对象
//赋值线程控制函数
UINTEvaluateThread(LPVOIDparam)
{
CSingleLocksinglelock;
singlelock(§ion);
//互斥区域
singlelock.Lock();
for(inti=0;i<10;i++)
array1[i]=i;
singlelock.Unlock();
}
//拷贝线程控制函数
UINTCopyThread(LPVOIDparam)
{
CSingleLocksinglelock;
singlelock(§ion);
//互斥区域
singlelock.Lock();
for(inti=0;i<10;i++)
array2[i]=array1[i];
singlelock.Unlock();
}
}
AfxBeginThread(EvaluateThread,NULL);//启动赋值线程
AfxBeginThread(CopyThread,NULL);//启动拷贝线程
上面的例子中启动了两个线程EvaluateThread和CopyThread,线程EvaluateThread把10个数赋值给数组array1[],线程CopyThread将数组array1[]拷贝给数组array2[]。
由于数组的拷贝和赋值都是整体行为,如果不以互斥形式执行代码段:
for(inti=0;i<10;i++)
array1[i]=i;
和
for(inti=0;i<10;i++)
array2[i]=array1[i];
其结果是很难预料的!
除了可使用CCriticalSection、CEvent、CMutex、CSemaphore作为线程间同步通信的方式以外,我们还可以利用PostThreadMessage函数在线程间发送消息:
BOOLPostThreadMessage(DWORDidThread,//threadidentifier
UINTMsg,//messagetopost
WPARAMwParam,//firstmessageparameter
LPARAMlParam//secondmessageparameter
3.线程与消息队列
在WIN32中,每一个线程都对应着一个消息队列。
由于一个线程可以产生数个窗口,所以并不是每个窗口都对应着一个消息队列。
下列几句话应该作为"定理"被记住:
"定理"一
所有产生给某个窗口的消息,都先由创建这个窗口的线程处理;
"定理"二
Windows屏幕上的每一个控件都是一个窗口,有对应的窗口函数。
消息的发送通常有两种方式,一是SendMessage,一是PostMessage,其原型分别为:
LRESULTSendMessage(HWNDhWnd,//handleofdestinationwindow
UINTMsg,//messagetosend
WPARAMwParam,//firstmessageparameter
LPARAMlParam//secondmessageparameter
);
BOOLPostMessage(HWNDhWnd,//handleofdestinationwindow
UINTMsg,//messagetopost
WPARAMwParam,//firstmessageparameter
LPARAMlParam//secondmessageparameter
);
两个函数原型中的四个参数的意义相同,但是SendMessage和PostMessage的行为有差异。
SendMessage必须等待消息被处理后才返回,而PostMessage仅仅将消息放入消息队列。
SendMessage的目标窗口如果属于另一个线程,则会发生线程上下文切换,等待另一线程处理完成消息。
为了防止另一线程当掉,导致SendMessage永远不能返回,我们可以调用SendMessageTimeout函数:
LRESULTSendMessageTimeo