友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
深入浅出MFC第2版(PDF格式)-第138部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
#0079 if (!(dwCreateFlags & CREATE_SUSPENDED))
#0080 VERIFY(pThread…》ResumeThread() != (DWORD)…1);
#0081
#0082 return pThread;
#0083 }
#0084
#0085 BOOL CWinThread::CreateThread (DWORD dwCreateFlags; UINT nStackSize;
#0086 LPSECURITY_ATTRIBUTES lpSecurityAttrs)
#0087 {
#0088 // setup startup structure for thread initialization
#0089 _AFX_THREAD_STARTUP startup; memset(&startup; 0; sizeof(startup));
#0090 startup。pThreadState = AfxGetThreadState();
#0091 startup。pThread = this;
#0092 startup。hEvent = ::CreateEvent(NULL; TRUE; FALSE; NULL);
758
…………………………………………………………Page 821……………………………………………………………
14 MFC
第 章 多緒程式設計
#0093 startup。hEvent2 = ::CreateEvent(NULL; TRUE; FALSE; NULL);
#0094 startup。dwCreateFlags = dwCreateFlags;
#0095 。。。
#0096 // create the thread (it may or may not start to run)
#0097 m_hThread = (HANDLE)_beginthreadex (lpSecurityAttrs; nStackSize;
#0098 &_AfxThreadEntry; &startup; dwCreateFlags | CREATE_SUSPENDED; (UINT*)&m_nThreadID);
#0099 。。。
#0100 }
图14…5 CWinThread 的相关源代码
产生执行线程, 为什么不直接用::CreateThread 或_beginthreadex ? 为什么要透过
CWinThread 对象? 我想你可以轻易从MFC 源代码中看出, 因为
CWinThread::CreateThread 和AfxBeginThread 不只是::CreateThread 的一层包装,更做
了一些application framework 所需的内部数据初始化工作,并确保使用正确的C runtime
library 版本。源代码中有:
#ifndef _MT
。。。 // 做些设定工作,不产生执行线程,回返。
#else
。。。 // 真正产生执行线程,回返。
#endif //!_MT)
的动作,只是被我删去未列出而已。
接下来我要把worker thread 和UI thread 的产生步骤做个整理。它们都需要调用
AfxBeginThread 以产生一个CWinThread 对象,但如果要产生一个UI thread ,你还必须
先定义一个CWinThread 衍生类别。
产生一个Worker Thread
Worker thread 不牵扯使用者接口。你应该为它准备一个执行线程函数, 然后调用
AfxBeginThread :
759
…………………………………………………………Page 822……………………………………………………………
第篇 深入 MFC 程式設計
CWinThread AfxBeginThread ThreadFunc
* pThread = ( ; &Param);
。。。
UINT ThreadFunc (LPVOID pParam)
{
。。。
}
AfxBeginThread 事实上一共可以接受六个参数,分别是:
CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc;
LPVOID pParam;
int nPriority = THREAD_PRIORITY_NORMAL;
UINT nStackSize = 0;
DWORD dwCreateFlags = 0;
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
参数一pfnThreadProc 表示执行线程函数。参数二pParam 表示要传给执行线程函数的参
数。参数三nPriority 表示优先权的微调值,预设为THREAD_PRIORITY_NORMAL ,也
就是没有微调。参数四nStackSize 表示堆栈的大小,默认值0 则表示堆栈最大容量为
1MB。参数五dwCreateFlags 如果为默认值0,就表示执行线程产生后立刻开始执行;如
果其值为CREATE_SUSPENDED ,就表示执行线程产生后先暂停执行。之后你可以使用
CWinThread::ResumeThread 重新执行它。参数六lpSecurityAttrs 代表新执行线程的安全防
护属性。默认值NULL 表示此一属性与其产生者(也是个执行线程)的属性相同。
在这里我们遭遇到一个困扰。执行线程函数是由系统调用的,也就是个callback 函数,不
容许有this 指针参数。所以任何一般的C++ 类别成员函数都不能够拿来当做执行线程函
式。它必须是个全域函数,或是个C++ 类别的static 成员函数。其原因我已经在第6
章的「Callback 函数」一节中描述过了,而采用全域函数或是C++ static 成员函数,其
间的优劣因素我也已经在该节讨论过。
执行线程函数的类型AFX_THREADPROC 定义于AFXWIN。H 之中:
// in AFXWIN。H
typedef UINT (AFX_CDECL *AFX_THREADPROC)(LPVOID);
所以你应该把本身的执行线程函数声明如下(其中的pParam 是个指针,在实用上可以指
向程序员自定的数据结构):
UINT ThreadFunc (LPVOID pParam);
760
…………………………………………………………Page 823……………………………………………………………
14 MFC
第 章 多緒程式設計
否则,编译时会获得这样的错误消息:
error C2665: 'AfxBeginThread' : none of the 2 overloads can convert
parameter 1 from type 'void (unsigned long *)'
有时候我们会让不同的执行线程使用相同的执行线程函数,这时候你就得特别注意到执行线程
函数使用全域变量或静态变量时,数据共享所引发的严重性(有好有坏)。至于放置在
堆栈中的变量或对象,都不会有问题,因为每一个执行线程自有一个堆栈。
产生一个UI Thread
UI thread 可不能够光由一个执行线程函数来代表,因为它要处理消息,它需要一个消息回
路。好得很,CWinThread::Run 里头就有一个消息循环。所以,我们应该先从CWinThread
衍生一个自己的类别,再调用AfxBeginThread 产生一个CWinThread 对象:
class CMyThread : public CWinThread
{
DECLARE_DYNCREATE(CMyThread)
public:
void BOOL InitInstance();
};
IMPLEMENT_DYNCREATE(CMyThread; CWinThread)
BOOL CMyThread::InitInstance()
{
。。。
}
CWinThread *pThread = AfxBeginThread(RUNTIME_CLASS(CMyThread));
我想你对RUNTIME_CLASS 宏已经不陌生了,第3章和第8章都有这个宏的源代码
展现以及意义解释。AfxBeginThread 是上一小节同名函数的一个overloaded 函数,一共
可以接受五个参数,分别是:
761
…………………………………………………………Page 824……………………………………………………………
第篇 深入 MFC 程式設計
CWinThread* AFXAPI AfxBeginThread(CRuntimeClass* pThreadClass;
int nPriority = THREAD_PRIORITY_NORMAL;
UINT nStackSize = 0;
DWORD dwCreateFlags = 0;
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
最后四个参数的意义和默认值比上一节同名函数相同,但是少接受一个LPVOID pParam
参数。
你可以在AFXWIN。H 中找到CWinThread 的定义:
class CWinThread : public CCmdTarget
{
DECLARE_DYNAMIC(CWinThread)
BOOL CreateThread(DWORD dwCreateFlags = 0; UINT nStackSize = 0;
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
。。。
int GetThreadPriority();
BOOL SetThreadPriority(int nPriority);
DWORD SuspendThread();
DWORD ResumeThread();
BOOL PostThreadMessage(UINT message; WPARAM wParam; LPARAM lParam);
。。。
};
其中有许多成员函数和图14…4 中的Win32 API 函数有关。在CWinThread 的成员函数
中,有五个函数只是非常单纯的Win32 API 的包装而已,它们被定义于AFXWIN2。INL
文件中:
// in AFXWIN2。INL
// CWinThread
_AFXWIN_INLINE BOOL CWinThread::SetThreadPriority (int nPriority)
{ ASSERT(m_hThread != NULL); return ::SetThreadPriority(m_hThread; nPriority); }
_AFXWIN_INLINE int CWinThread::GetThreadPriority ()
{ ASSERT(m_hThread != NULL); return ::GetThreadPriority (m_hThread); }
CWinThread ResumeThread
_AFXWIN_INLINE DWORD :: ()
{ ASSERT(m_hThread != NULL); return ::ResumeThread (m_hThread); }
_AFXWIN_INLINE DWORD CWinThread::SuspendThread ()
{ ASSERT(m_hThread != NULL); return ::SuspendThread (m_hThread); }
_AFXWIN_INLINE BOOL CWinThread::PostThreadMessage (UINT message; WPARAM wParam; LPARAM
lParam)
{ ASSERT(m_hThread != NULL); return ::PostThreadMessage (m_nThreadID; message; wParam;
lParam); }
762
…………………………………………………………Page 825……………………………………………………………
14 MFC
第 章 多緒程式設計
执行线程的结束
既然worker thread 的生命就是执行线程函数本身,函数一旦return ,执行线程也就结束了,
自然得很。或者执行线程函数也可以调用AfxEndThread ,结束一个执行线程。
UI 执行线程因为有消息循环的关系,必须在消息队列中放一个WM_QUIT,才能结束执行
线程。放置的方式和一般Win32 程序一样,调用::PostQuitMessage 即可办到。亦或者,
在执行线程的任何一个函数中调用AfxEndThread ,也可以结束执行线程。
AfxEndThread 其实也是个外包装,其内部调用_endthreadex,这个动作才真正把执行线程
结束掉。
别忘了,不论worker thread 或UI thread,都需要一个CWinThread 对象,当执行线程结
束,记得把该对象释放掉(利用delete )。
执行线程与同步控制
看起来执行线程的诞生与结束,以及对它的优先权设定、冻结、重新激活,都很容易。但
是我必须警告你,多线程程序的设计成功关键并不在此。如果你的每一个执行线程都非常独
立,彼此没有干联,也就罢了
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!