⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chap12.html

📁 VC实例 通过经典实例讲解 是word格式的 阅读方便
💻 HTML
📖 第 1 页 / 共 5 页
字号:
<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>&#9;int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, </P>
<P>&#9;DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );</P>
<P>CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass, </P>
<P>&#9;int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, </P>
<P>&#9;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>&#9;// TODO: Add your command handler code here</P>
<P>&#9;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)&#9;</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.&#9;</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 + -