?? chap12.html
字號(hào):
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=gb_2312-80">
<META NAME="Generator" CONTENT="Microsoft Word 97">
<TITLE>第二章 多線程</TITLE>
</HEAD>
<BODY>
<FONT FACE="黑體" LANG="ZH-CN" SIZE=5><P ALIGN="CENTER"><A NAME="_Toc425699146">第十二章</FONT><FONT FACE="Arial" SIZE=5> </FONT><FONT FACE="黑體" LANG="ZH-CN" SIZE=5>多線程</A></P>
</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY">當(dāng)使用</FONT><FONT SIZE=3>Windows 95</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>或者其它現(xiàn)在比較流行的操作系統(tǒng)時(shí),可以同時(shí)運(yùn)行幾個(gè)程序,這是大家都知道的。操作系統(tǒng)的這種能力稱(chēng)之為多任務(wù)處理。現(xiàn)今的許多操作系統(tǒng)也支持線程。一個(gè)應(yīng)用程序能夠創(chuàng)建幾個(gè)線程。線程能夠使你在多任務(wù)中進(jìn)行多任務(wù)。一般的用戶知道他能夠在同一時(shí)刻運(yùn)行多個(gè)程序,而編程者知道一個(gè)程序可以在同一時(shí)刻運(yùn)行幾個(gè)線程。在本章中,你將學(xué)會(huì)如何在你的程序中創(chuàng)建和管理線程。具體的說(shuō),包含以下內(nèi)容:</P>
<UL>
<P ALIGN="JUSTIFY"><LI>創(chuàng)建線程</LI></P>
<P ALIGN="JUSTIFY"><LI>線程間通信</LI></P>
<P ALIGN="JUSTIFY"><LI>線程同步</LI></P></UL>
</FONT><FONT FACE="仿宋_GB2312" LANG="ZH-CN" SIZE=4><P ALIGN="CENTER"><A NAME="_Toc425699147">第一節(jié)</FONT><FONT SIZE=4> </FONT><FONT FACE="仿宋_GB2312" LANG="ZH-CN" SIZE=4>創(chuàng)建線程</A></P>
</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY">線程就是操作系統(tǒng)分配處理器時(shí)間的最基本單元。在一個(gè)多線程的應(yīng)用程序中,每一個(gè)線程都有它自己的堆棧,并且可以獨(dú)立的操作在同一程序中運(yùn)行的其它線程。</FONT><FONT SIZE=3>MFC</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>支持兩種線程類(lèi)型:用戶接口線程和工人線程。前者有自己的消息泵,可以處理用戶接口的任務(wù),而后者則不能,它是最常用的線程。</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY"> </FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>一個(gè)應(yīng)用程序至少有一個(gè)線程,即程序的基本或主線程。你可以根據(jù)需要啟動(dòng)和停止其它附加線程,但是一旦主線程停止了,整個(gè)程序就被關(guān)閉了。只要程序還在運(yùn)行,主線程就在運(yùn)行。</P>
<P ALIGN="JUSTIFY">為了使用</FONT><FONT SIZE=3>MFC</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>創(chuàng)建一個(gè)線程,你所做的就是編寫(xiě)一個(gè)你希望的和程序的其它部分同時(shí)運(yùn)行的函數(shù),然后調(diào)用</FONT><FONT SIZE=3>AfxBeginThread</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>()來(lái)啟動(dòng)一個(gè)用以執(zhí)行你的函數(shù)的線程。只要線程的函數(shù)在運(yùn)行,線程就存活著,當(dāng)線程函數(shù)結(jié)束時(shí),線程就被銷(xiāo)毀。</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">AfxBeginThread</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>()函數(shù)如下所示:</P>
</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=1><P>CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc, LPVOID pParam, </P>
<P>	int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, </P>
<P>	DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );</P>
<P>CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass, </P>
<P>	int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, </P>
<P>	DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );</P>
</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY">第一種形式用于創(chuàng)建工人線程,第二種線程用于創(chuàng)建用戶接口線程。</P>
<P ALIGN="JUSTIFY">這兩種形式的函數(shù)的返回值是新創(chuàng)建的線程對(duì)象的指針。</P>
<P ALIGN="JUSTIFY">參數(shù)意義如下:</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">pfnThreadProc</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>:指向工人線程的控制函數(shù)的指針,它不能是</FONT><FONT SIZE=3>NULL</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>。此控制函數(shù)必須聲明成如下樣式:</P>
</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=1><P>UINT MyControllingFunction( LPVOID pParam );</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">pThreadClass</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>:從</FONT><FONT SIZE=3>CWinThread</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>派生的</FONT><FONT SIZE=3>RUNTIME_CLASS</P>
<P ALIGN="JUSTIFY">pParam</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>:傳遞給工人線程的控制函數(shù)的參數(shù)</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">nPriority</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>:線程的期望的優(yōu)先權(quán)。如果這個(gè)值為</FONT><FONT SIZE=3>0</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>,則新線程和創(chuàng)建線程具有同樣的優(yōu)先級(jí)。</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">nStackSize</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>:以字節(jié)為單位定義了新線程的堆棧大小。如果這個(gè)值為</FONT><FONT SIZE=3>0</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>,則新線程和創(chuàng)建線程具有同樣大小的堆棧。</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">dwCreateFlags</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>:控制線程創(chuàng)建的附加標(biāo)志。這個(gè)值可以是以下兩個(gè)值中的一個(gè):</FONT><FONT SIZE=3>CREATE_SUSPENDED</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>和</FONT><FONT SIZE=3>0</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>。如果是標(biāo)志是前者,以掛起數(shù)為</FONT><FONT SIZE=3>1</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>啟動(dòng)線程。只有在</FONT><FONT SIZE=3>ResumeThread</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>被調(diào)用時(shí),這個(gè)線程才會(huì)被執(zhí)行。如果標(biāo)志為</FONT><FONT SIZE=3>0</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>,則在創(chuàng)建線程后立即執(zhí)行線程。</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">lpSecurityAttrs</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>:指向定義了線程安全屬性的</FONT><FONT SIZE=3>SECURITY_ATTRIBUTES</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>結(jié)構(gòu)的指針。如果為</FONT><FONT SIZE=3>NULL</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>,則新線程和創(chuàng)建線程具有同樣的安全屬性。</P>
<P ALIGN="JUSTIFY">線程可能具有下面的優(yōu)先級(jí)別:</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">THREAD_PRIORITY_ABOVE_NORMAL </FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>比正常優(yōu)先級(jí)高一個(gè)級(jí)別</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">THREAD_PRIORITY_BELOW_NORMAL </FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>比正常優(yōu)先級(jí)低一個(gè)級(jí)別</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">THREAD_PRIORITY_HIGHEST </FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>比正常優(yōu)先級(jí)高兩個(gè)級(jí)別</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">THREAD_PRIORITY_IDLE </FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>基本優(yōu)先級(jí)為</FONT><FONT SIZE=3>1</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>。對(duì)于</FONT><FONT SIZE=3>REALTIME_PRIORITY_CLASS</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>進(jìn)程,優(yōu)先級(jí)為</FONT><FONT SIZE=3>16</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>。</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">THREAD_PRIORITY_LOWEST </FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>比正常優(yōu)先級(jí)低兩個(gè)級(jí)別</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">THREAD_PRIORITY_NORMAL </FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>正常優(yōu)先級(jí)別</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">THREAD_PRIORITY_TIME_CRITICAL </FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>基本優(yōu)先級(jí)為</FONT><FONT SIZE=3>15</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>。對(duì)于</FONT><FONT SIZE=3>REALTIME_PRIORITY_CLASS</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>進(jìn)程,優(yōu)先級(jí)別是</FONT><FONT SIZE=3>30</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>。</P>
<P ALIGN="JUSTIFY">一個(gè)線程的優(yōu)先級(jí)決定了相對(duì)于其它正在運(yùn)行的線程這個(gè)線程控制系統(tǒng)的時(shí)間。通常,線程的級(jí)別越高,它的運(yùn)行時(shí)間也越長(zhǎng),這也正是</FONT><FONT SIZE=3>THREAD_PRIORITY_TIME_CRITICAL</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>如此高的原因。</P>
<P ALIGN="JUSTIFY"></P>
<P ALIGN="JUSTIFY">下面用一個(gè)簡(jiǎn)單的例子說(shuō)明如何創(chuàng)建線程,按照下面的步驟進(jìn)行:</P>
<P ALIGN="JUSTIFY">使用</FONT><FONT SIZE=3>MFC AppWizard</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>生成一個(gè)單文檔應(yīng)用程序</FONT><FONT SIZE=3>Thread</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>。</P>
<P ALIGN="JUSTIFY">使用資源編輯器編輯器給程序的</FONT><FONT SIZE=3>IDR_MAINFRAME</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>菜單添加一個(gè)菜單“線程”。</P>
<P ALIGN="JUSTIFY">在“線程”菜單中添加一個(gè)菜單項(xiàng)啟動(dòng)線程,其</FONT><FONT SIZE=3>ID</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>為</FONT><FONT SIZE=3>ID_STARTTHREAD</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>。</P>
<P ALIGN="JUSTIFY">在</FONT><FONT SIZE=3>CThreadView</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>類(lèi)中添加消息映射函數(shù)</FONT><FONT SIZE=3>OnStartthread()</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>。</P>
<P ALIGN="JUSTIFY">在</FONT><FONT SIZE=3>OnStartthread()</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>函數(shù)中添加如下代碼:</P>
</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=1><P>void CThreadView::OnStartthread() </P>
<P>{</P>
<P>	// TODO: Add your command handler code here</P>
<P>	HWND hWnd = GetSafeHwnd();</P>
<P> AfxBeginThread(ThreadProc, hWnd, THREAD_PRIORITY_NORMAL);</P>
<P>}</P>
</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY">添加的代碼將調(diào)用</FONT><FONT SIZE=3>ThreadProc()</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>,這個(gè)函數(shù)是新添加的線程的控制函數(shù),所以還需要在程序中添加這個(gè)函數(shù)。</P>
<P ALIGN="JUSTIFY">在</FONT><FONT SIZE=3>ThreadView.cpp</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>中</FONT><FONT SIZE=3>OnStartthread()</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>的上面添加函數(shù)</FONT><FONT SIZE=3>ThreadProc()</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>。</P>
<UL>
</FONT><FONT FACE="黑體" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY"><LI>注意:</LI></P>
</FONT><FONT FACE="楷體_GB2312" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY"><LI>這個(gè)函數(shù)是一個(gè)全局函數(shù),而并非是</FONT><FONT SIZE=3>CThreadView</FONT><FONT FACE="楷體_GB2312" LANG="ZH-CN" SIZE=3>類(lèi)的成員函數(shù),盡管它在</FONT><FONT SIZE=3>CThreadView</FONT><FONT FACE="楷體_GB2312" LANG="ZH-CN" SIZE=3>類(lèi)的執(zhí)行文件中。</LI></P></UL>
</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY"></P>
<P ALIGN="JUSTIFY">在函數(shù)</FONT><FONT SIZE=3>ThreadProc()</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>中添加如下代碼:</P>
</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=1><P>UINT ThreadProc(LPVOID param)</P>
<P>{</P>
<P> ::MessageBox((HWND)param, "Thread activated.", "Thread", MB_OK);</P>
<P> return 0;</P>
<P>}</P>
</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY">這個(gè)線程實(shí)際上并沒(méi)有作什么,它僅僅報(bào)告它被啟動(dòng)了。</P>
<P ALIGN="JUSTIFY">在函數(shù)前面的兩個(gè)冒號(hào)表明是在調(diào)用全局函數(shù),對(duì)于</FONT><FONT SIZE=3>Windows</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>程序員來(lái)說(shuō),這通常稱(chēng)為</FONT><FONT SIZE=3>API</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>或</FONT><FONT SIZE=3>SDK</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>調(diào)用。</P>
<P ALIGN="JUSTIFY">當(dāng)你運(yùn)行這個(gè)程序后,主窗口出現(xiàn)。選擇“線程”菜單中的“啟動(dòng)線程”菜單選項(xiàng),系統(tǒng)啟動(dòng)一個(gè)線程,并且顯示一個(gè)消息框,如圖</FONT><FONT SIZE=3>12.1</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>所示。</P>
<P ALIGN="CENTER"><IMG SRC="Image438.gif" tppabs="http://166.111.167.223/computer/cai/visual_c++_5.0_programming/Image438.gif" WIDTH=85 HEIGHT=62></P>
</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=1><P ALIGN="CENTER">圖</FONT><FONT SIZE=1>12. 1 </FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=1>線程啟動(dòng)消息框</P>
</FONT><FONT FACE="仿宋_GB2312" LANG="ZH-CN" SIZE=4><P ALIGN="CENTER"><A NAME="_Toc425699148">第二節(jié)</FONT><FONT SIZE=4> </FONT><FONT FACE="仿宋_GB2312" LANG="ZH-CN" SIZE=4>線程間通信</A></P>
</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY">通常,一個(gè)次要的線程為主線程執(zhí)行一定的任務(wù),這也暗示這在主線程和次要線程之間需要有一個(gè)聯(lián)系的渠道。有幾種方法可以完成這些聯(lián)系任務(wù):使用全局變量、使用</FONT><FONT SIZE=3>CEven</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>類(lèi)或者使用消息。本節(jié)將介紹這幾種方法。</P><DIR>
</FONT><FONT FACE="Arial" SIZE=3><P>(1)	</FONT><FONT FACE="黑體" LANG="ZH-CN" SIZE=3>使用全局變量通信</P></DIR>
</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY">假定你需要你的程序能夠停止線程。你需要一個(gè)告訴線程何時(shí)停止的方法。一種方法是建立一個(gè)全局變量,然后讓線程監(jiān)督這個(gè)全局變量是否為標(biāo)志線程終止的值。為了實(shí)現(xiàn)這種方法,按照如下步驟修改前面創(chuàng)建的</FONT><FONT SIZE=3>Thread</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>程序。</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">1.	</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>在“線程”菜單中添加菜單項(xiàng)“停止線程”,</FONT><FONT SIZE=3>ID</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>為</FONT><FONT SIZE=3>ID_STOPTHREAD</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>。</P>
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -