?? mfc
字號:
<P></P>
<P align=justify></P>
<LI>使用AfxBeginThread創建MFC線程對象和Win32線程對象。如果創建線程時沒有指定CREATE_SUSPENDED,則開始執行線程。
<P></P>
<P align=justify></P>
<LI>如果創建線程是指定了CREATE_SUSPENDED,則在適當的地方調用函數ResumeThread開始執行線程。
<P></P></LI></UL>
<OL>
<OL>
<OL>
<P align=justify>
<LI><A name=_Toc445889090></A><A name=_Toc445782493></A><A
name=_Toc452640954></A><A name=_Toc457299052></A><B>創建工作者線程</B>
<P></P>
<P align=justify>程序員不必從CWinThread派生新的線程類,只需要提供一個控制函數,由線程啟動后執行該函數。</P>
<P
align=justify>然后,使用AfxBeginThread創建MFC線程對象和Win32線程對象。如果創建線程時沒有指定CREATE_SUSPENDED(創建后掛起),則創建的新線程開始執行。</P>
<P
align=justify>如果創建線程是指定了CREATE_SUSPENDED,則在適當的地方調用函數ResumeThread開始執行線程。</P>
<P align=justify>雖然程序員沒有從CWinThread派生類,但是MFC給工作者線程提供了缺省的CWinThread對象。</P>
<P align=justify></P>
<LI><A name=_Toc445889091></A><A name=_Toc445782494></A><A
name=_Toc452640955></A><A name=_Toc457299053></A><B>AfxBeginThread</B>
<P></P></LI></OL></OL></OL>
<P
align=justify>用戶界面線程和工作者線程都是由AfxBeginThread創建的。現在,考察該函數:MFC提供了兩個重載版的AfxBeginThread,一個用于用戶界面線程,另一個用于工作者線程,分別有如下的原型和過程:</P>
<OL>
<P align=justify>
<LI>用戶界面線程的AfxBeginThread
<P></P>
<P align=justify>用戶界面線程的AfxBeginThread的原型如下:</P>
<P align=justify>CWinThread* AFXAPI AfxBeginThread(</P>
<P align=justify>CRuntimeClass* pThreadClass,</P>
<P align=justify>int nPriority, </P>
<P align=justify>UINT nStackSize, </P>
<P align=justify>DWORD dwCreateFlags,</P>
<P align=justify>LPSECURITY_ATTRIBUTES lpSecurityAttrs)</P>
<P align=justify>其中:</P>
<P align=justify>參數1是從CWinThread派生的RUNTIME_CLASS類;</P>
<P align=justify>參數2指定線程優先級,如果為0,則與創建該線程的線程相同;</P>
<P align=justify>參數3指定線程的堆棧大小,如果為0,則與創建該線程的線程相同;</P>
<P
align=justify>參數4是一個創建標識,如果是CREATE_SUSPENDED,則在懸掛狀態創建線程,在線程創建后線程掛起,否則線程在創建后開始線程的執行。</P>
<P align=justify>參數5表示線程的安全屬性,NT下有用。</P>
<P align=justify></P>
<LI>工作者線程的AfxBeginThread
<P></P>
<P align=justify>工作者線程的AfxBeginThread的原型如下:</P>
<P align=justify>CWinThread* AFXAPI AfxBeginThread(</P>
<P align=justify>AFX_THREADPROC pfnThreadProc, </P>
<P align=justify>LPVOID pParam,</P>
<P align=justify>int nPriority, </P>
<P align=justify>UINT nStackSize, </P>
<P align=justify>DWORD dwCreateFlags,</P>
<P align=justify>LPSECURITY_ATTRIBUTES lpSecurityAttrs)</P>
<P align=justify>其中:</P>
<P align=justify>參數1指定控制函數的地址;</P>
<P align=justify>參數2指定傳遞給控制函數的參數;</P>
<P align=justify>參數3、4、5分別指定線程的優先級、堆棧大小、創建標識、安全屬性,含義同用戶界面線程。</P>
<P align=justify></P>
<LI>AfxBeginThread創建線程的流程
<P></P></LI></OL>
<P
align=justify>不論哪個AfxBeginThread,首先都是創建MFC線程對象,然后創建Win32線程對象。在創建MFC線程對象時,用戶界面線程和工作者線程的創建分別調用了不同的構造函數。用戶界面線程是從CWinThread派生的,所以,要先調用派生類的缺省構造函數,然后調用CWinThread的缺省構造函數。圖8-1中兩個構造函數所調用的CommonConstruct是MFC內部使用的成員函數。</P><IMG
height=468 hspace=12 src="MFC教程8_ MFC的進程和線程.files/image140.gif" width=349
align=left>
<OL>
<OL>
<OL>
<P align=justify>
<LI><A name=_Toc445889092></A><A name=_Toc445782495></A><A
name=_Toc452640956></A><A
name=_Toc457299054></A><B>CreateThread和_AfxThreadEntry</B>
<P></P>
<P
align=justify>MFC使用CWinThread::CreateThread創建線程,不論對工作者線程或用戶界面線程,都指定線程的入口函數是_AfxThreadEntry。_AfxThreadEntry調用AfxInitThread初始化線程。</P>
<P
align=justify>CreateThread和_AfxThreadEntry在線程的創建過程中使用同步手段交互等待、執行。CreateThread由創建線程執行,_AfxThreadEntry由被創建的線程執行,兩者通過兩個事件對象(hEvent和hEvent2)同步:</P>
<P
align=justify>在創建了新線程之后,創建線程將在hEvent事件上無限等待直到新線程給出創建結果;新線程在創建成功或者失敗之后,觸發事件hEvent讓父線程運行,并且在hEven2上無限等待直到父線程退出CreateThread函數;父線程(創建線程)因為hEvent的置位結束等待,繼續執行,退出CreateThread之前觸發hEvent2事件;新線程(子線程)因為hEvent2的置位結束等待,開始執行控制函數(工作者線程)或者進入消息循環(用戶界面線程)。</P>
<P align=justify>MFC在線程創建中使用了如下數據結構:</P>
<P align=justify>struct _AFX_THREAD_STARTUP</P>
<P align=justify>{</P>
<P align=justify>//傳遞給線程啟動的參數(IN)</P>
<P align=justify>_AFX_THREAD_STATE* pThreadState;//父線程的線程狀態</P>
<P align=justify>CWinThread* pThread; //新創建的MFC線程對象</P>
<P align=justify>DWORD dwCreateFlags; //線程創建標識</P>
<P align=justify>_PNH pfnNewHandler; //新線程的句柄</P>
<P align=justify>HANDLE hEvent; //同步事件,線程創建成功或失敗后置位</P>
<P align=justify>HANDLE hEvent2; //同步事件,新線程恢復執行后置位</P>
<P align=justify></P>
<P align=justify>//返回給創建線程的參數,在新線程恢復執行后賦值</P>
<P align=justify>BOOL bError; //如果創建發生錯誤,TRUE</P>
<P align=justify>};</P>
<P
align=justify>該結構作為線程開始函數的參數被傳遞給_beginthreadex函數來創建和啟動線程。_beginthreadex函數是“C”的線程創建函數,具有如下原型:</P>
<P align=justify>unsigned long _beginthreadex(</P>
<P align=justify>void *security,</P>
<P align=justify>unsigned stack_size,</P>
<P align=justify>unsigned ( __stdcall *start_address )( void * ),</P>
<P align=justify>void *arglist,</P>
<P align=justify>unsigned initflag,</P>
<P align=justify>unsigned *thrdaddr );</P>
<P align=justify></P>
<P
align=justify>圖8-2描述了上述過程。圖中表示,_AfxThreadEntry在啟動線程時,將創建本線程的線程狀態,并且繼承父線程的模塊狀態。關于MFC狀態,見第9章。</P>
<P align=justify></P><IMG height=750 hspace=12
src="MFC教程8_ MFC的進程和線程.files/image141.gif" width=384 align=left>
<P align=justify></P>
<P align=justify> </P><IMG height=531 hspace=12
src="MFC教程8_ MFC的進程和線程.files/image142.gif" width=433 align=left>
<P align=justify> </P>
<P align=justify></P>
<LI><A name=_Toc445889093></A><A name=_Toc445782496></A><A
name=_Toc452640957></A><A name=_Toc457299055></A><B>線程的結束</B>
<P></P>
<P
align=justify>從圖8-2可以看出,AfxEndThread用來結束調用它的線程:它將清理本線程創建的MFC對象和釋放線程局部存儲分配的內存空間;調用CWinThread的虛擬函數Delete;調用“C”的結束線程函數_endthreadex釋放分配給線程的資源,但是不關閉線程句柄。</P>
<P
align=justify>CWinThread::Delete的缺省實現是:如果本線程的成員函數m_bDelete為TRUE,則調用“C”運算符號delete銷毀MFC線程對象自身(delete
this),這將導致線程對象的析構函數被調用。若析構函數檢測線程句柄非空則調用CloseHandle關閉它。</P>
<P
align=justify>通常,讓m_bDelete為TRUE以便自動地銷毀線程對象,釋放內存空間(MFC內存對象在堆中分配)。但是,有時候,在線程結束之后(Win32線程已經不存在)保留MFC線程對象是有用的,當然程序員自己最后要記得銷毀該線程對象。</P>
<P align=justify></P>
<LI><A name=_Toc445889094></A><A name=_Toc445782497></A><A
name=_Toc452640958></A><A name=_Toc457299056></A><B>實現線程的消息循環</B>
<P></P></LI></OL></OL></OL>
<P
align=justify>在MFC中,消息循環是由線程完成的。一般地,可以使用MFC缺省的消息循環(即使用函數CWindThrad::Run),但是,有些時候需要程序員自己實現一個線程的消息循環,比如在用戶界面線程進行一個長時間計算處理或者等待另一個線程時。一般有如下形式:</P>
<P align=justify>while ( bDoingBackgroundProcessing) </P>
<P align=justify>{</P>
<P align=justify>MSG msg;</P>
<P align=justify>while ( ::PeekMessage( &msg, NULL,0, 0, PM_NOREMOVE ) )</P>
<P align=justify>{</P>
<DIR>
<P align=justify>if ( !PumpMessage( ) )</P>
<P align=justify>{</P>
<DIR>
<P align=justify>bDoingBackgroundProcessing = FALSE; </P>
<P align=justify>::PostQuitMessage( );</P>
<P align=justify>break;</P></DIR>
<P align=justify>}</P></DIR>
<P align=justify>}</P>
<P align=justify>// let MFC do its idle processing</P>
<P align=justify>LONG lIdle = 0;</P>
<P align=justify>while ( AfxGetApp()->OnIdle(lIdle++ ) );</P>
<P align=justify>// Perform some background processing here </P>
<P align=justify>// using another call to OnIdle</P>
<P align=justify>}</P>
<P align=justify>該段代碼的解釋參見圖5-3對線程的Run函數的圖解。</P>
<P align=justify></P>
<P
align=justify>程序員實現線程的消息循環有兩個好處,一是顧及了MFC的Idle處理機制;二是在長時間的處理中可以響應用戶產生的事件或者消息。</P>
<P align=justify>在同步對象上等待其他線程時,也可以使用同樣的方式,只要把條件</P>
<P align=justify>bDoingBackgroundProcessing</P>
<P align=justify>換成如下形式:</P>
<P align=justify>WaitForSingObject(hHandleOfEvent,0) == WAIT_TIMEOUT</P>
<P align=justify>即可。</P>
<P align=justify>MFC處理線程和進程時還引入了一個重要的概念:狀態,如線程狀態(Thread State)、進程狀態(Process
State)、模塊狀態(Module State)等。由于這個概念在MFC中占有重要地位,涉及的內容比較多,所以專門在下一章來講述它。</P>
<HR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" align=center border=0>
<TBODY>
<TR>
<TD align=middle><A href="http://www.vczx.com/tutorial/mfc/mfc7.php"
target=_self>上一章</A> <A href="http://www.vczx.com/tutorial/mfc/mfc.php"
target=_self>回目錄</A> <A href="http://www.vczx.com/tutorial/mfc/mfc9.php"
target=_self>下一章</A></TD></TR></TBODY></TABLE>
<P> </P>
<P align=justify></P></BODY></HTML>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -