?? chap12.html
字號:
<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">當使用</FONT><FONT SIZE=3>Windows 95</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>或者其它現在比較流行的操作系統時,可以同時運行幾個程序,這是大家都知道的。操作系統的這種能力稱之為多任務處理。現今的許多操作系統也支持線程。一個應用程序能夠創建幾個線程。線程能夠使你在多任務中進行多任務。一般的用戶知道他能夠在同一時刻運行多個程序,而編程者知道一個程序可以在同一時刻運行幾個線程。在本章中,你將學會如何在你的程序中創建和管理線程。具體的說,包含以下內容:</P>
<UL>
<P ALIGN="JUSTIFY"><LI>創建線程</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">第一節</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">線程就是操作系統分配處理器時間的最基本單元。在一個多線程的應用程序中,每一個線程都有它自己的堆棧,并且可以獨立的操作在同一程序中運行的其它線程。</FONT><FONT SIZE=3>MFC</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>支持兩種線程類型:用戶接口線程和工人線程。前者有自己的消息泵,可以處理用戶接口的任務,而后者則不能,它是最常用的線程。</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY"> </FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>一個應用程序至少有一個線程,即程序的基本或主線程。你可以根據需要啟動和停止其它附加線程,但是一旦主線程停止了,整個程序就被關閉了。只要程序還在運行,主線程就在運行。</P>
<P ALIGN="JUSTIFY">為了使用</FONT><FONT SIZE=3>MFC</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>創建一個線程,你所做的就是編寫一個你希望的和程序的其它部分同時運行的函數,然后調用</FONT><FONT SIZE=3>AfxBeginThread</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>()來啟動一個用以執行你的函數的線程。只要線程的函數在運行,線程就存活著,當線程函數結束時,線程就被銷毀。</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">AfxBeginThread</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>()函數如下所示:</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">第一種形式用于創建工人線程,第二種線程用于創建用戶接口線程。</P>
<P ALIGN="JUSTIFY">這兩種形式的函數的返回值是新創建的線程對象的指針。</P>
<P ALIGN="JUSTIFY">參數意義如下:</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">pfnThreadProc</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>:指向工人線程的控制函數的指針,它不能是</FONT><FONT SIZE=3>NULL</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>。此控制函數必須聲明成如下樣式:</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>:傳遞給工人線程的控制函數的參數</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">nPriority</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>:線程的期望的優先權。如果這個值為</FONT><FONT SIZE=3>0</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>,則新線程和創建線程具有同樣的優先級。</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">nStackSize</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>:以字節為單位定義了新線程的堆棧大小。如果這個值為</FONT><FONT SIZE=3>0</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>,則新線程和創建線程具有同樣大小的堆棧。</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">dwCreateFlags</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>:控制線程創建的附加標志。這個值可以是以下兩個值中的一個:</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>。如果是標志是前者,以掛起數為</FONT><FONT SIZE=3>1</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>啟動線程。只有在</FONT><FONT SIZE=3>ResumeThread</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>被調用時,這個線程才會被執行。如果標志為</FONT><FONT SIZE=3>0</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>,則在創建線程后立即執行線程。</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>結構的指針。如果為</FONT><FONT SIZE=3>NULL</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>,則新線程和創建線程具有同樣的安全屬性。</P>
<P ALIGN="JUSTIFY">線程可能具有下面的優先級別:</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">THREAD_PRIORITY_ABOVE_NORMAL </FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>比正常優先級高一個級別</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">THREAD_PRIORITY_BELOW_NORMAL </FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>比正常優先級低一個級別</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">THREAD_PRIORITY_HIGHEST </FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>比正常優先級高兩個級別</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">THREAD_PRIORITY_IDLE </FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>基本優先級為</FONT><FONT SIZE=3>1</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>。對于</FONT><FONT SIZE=3>REALTIME_PRIORITY_CLASS</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>進程,優先級為</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>比正常優先級低兩個級別</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">THREAD_PRIORITY_NORMAL </FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>正常優先級別</P>
</FONT><FONT SIZE=3><P ALIGN="JUSTIFY">THREAD_PRIORITY_TIME_CRITICAL </FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>基本優先級為</FONT><FONT SIZE=3>15</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>。對于</FONT><FONT SIZE=3>REALTIME_PRIORITY_CLASS</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>進程,優先級別是</FONT><FONT SIZE=3>30</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>。</P>
<P ALIGN="JUSTIFY">一個線程的優先級決定了相對于其它正在運行的線程這個線程控制系統的時間。通常,線程的級別越高,它的運行時間也越長,這也正是</FONT><FONT SIZE=3>THREAD_PRIORITY_TIME_CRITICAL</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>如此高的原因。</P>
<P ALIGN="JUSTIFY"></P>
<P ALIGN="JUSTIFY">下面用一個簡單的例子說明如何創建線程,按照下面的步驟進行:</P>
<P ALIGN="JUSTIFY">使用</FONT><FONT SIZE=3>MFC AppWizard</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>生成一個單文檔應用程序</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>菜單添加一個菜單“線程”。</P>
<P ALIGN="JUSTIFY">在“線程”菜單中添加一個菜單項啟動線程,其</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>類中添加消息映射函數</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>函數中添加如下代碼:</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">添加的代碼將調用</FONT><FONT SIZE=3>ThreadProc()</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>,這個函數是新添加的線程的控制函數,所以還需要在程序中添加這個函數。</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>的上面添加函數</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>這個函數是一個全局函數,而并非是</FONT><FONT SIZE=3>CThreadView</FONT><FONT FACE="楷體_GB2312" LANG="ZH-CN" SIZE=3>類的成員函數,盡管它在</FONT><FONT SIZE=3>CThreadView</FONT><FONT FACE="楷體_GB2312" LANG="ZH-CN" SIZE=3>類的執行文件中。</LI></P></UL>
</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3><P ALIGN="JUSTIFY"></P>
<P ALIGN="JUSTIFY">在函數</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">這個線程實際上并沒有作什么,它僅僅報告它被啟動了。</P>
<P ALIGN="JUSTIFY">在函數前面的兩個冒號表明是在調用全局函數,對于</FONT><FONT SIZE=3>Windows</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>程序員來說,這通常稱為</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>調用。</P>
<P ALIGN="JUSTIFY">當你運行這個程序后,主窗口出現。選擇“線程”菜單中的“啟動線程”菜單選項,系統啟動一個線程,并且顯示一個消息框,如圖</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>線程啟動消息框</P>
</FONT><FONT FACE="仿宋_GB2312" LANG="ZH-CN" SIZE=4><P ALIGN="CENTER"><A NAME="_Toc425699148">第二節</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">通常,一個次要的線程為主線程執行一定的任務,這也暗示這在主線程和次要線程之間需要有一個聯系的渠道。有幾種方法可以完成這些聯系任務:使用全局變量、使用</FONT><FONT SIZE=3>CEven</FONT><FONT FACE="宋體" LANG="ZH-CN" SIZE=3>類或者使用消息。本節將介紹這幾種方法。</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">假定你需要你的程序能夠停止線程。你需要一個告訴線程何時停止的方法。一種方法是建立一個全局變量,然后讓線程監督這個全局變量是否為標志線程終止的值。為了實現這種方法,按照如下步驟修改前面創建的</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>在“線程”菜單中添加菜單項“停止線程”,</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>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -