Programming Windows程式开发设计指南 第20章 多工和多执行绪

20. 多工和多执行绪

织梦好,好织梦

多工是一个作业系统可以同时执行多个程式的能力。基本上,作业系统使用一个硬体时钟为同时执行的每个程序配置「时间片段」。如果时间片段够小,并且机器也没有由於太多的程式而超出负荷时,那么在使用者看来,所有的这些程式似乎在同时执行著。 copyright dedecms

多工并不是什么新的东西。在大型电脑上,多工是必然的。这些大型主机通常有几十甚至几百个终端机和它连结,而每个终端机使用者都应该感觉到他或者她独占了整个电脑。另外,大型主机的作业系统通常允许使用者「提交工作到背景」,这些背景作业可以在使用者进行其他工作时,由机器执行完成。

copyright dedecms

个人电脑上的多工花了更长的时间才普及化。但是现在PC多工也被认为是很正常的了。我马上就会讨论到,Microsoft Windows16位元版本支援有限度的多工,Windows32位元版本支援真正的多工,而且,还多了一种额外的优点,多执行绪。

内容来自dedecms

多执行绪是在一个程式内部实作多工的能力。程式可以把它自己分隔为各自独立的「执行绪」,这些执行绪似乎也同时在执行著。这一概念初看起来似乎没有什么用处,但是它可以让程式使用多执行绪在背景执行冗长作业,从而让使用者不必长时间地无法使用其电脑进行其他工作(有时这也许不是人们所希望的,不过这种时候去冲冲凉或者到冰箱去看看总是很不错的)!但是,即使在电脑繁忙的时候,使用者也应该能够使用它。 dedecms.com

多工的各种模式
  copyright dedecms

PC的早期,有人曾经提倡未来应该朝多工的方向前进,但是大多数的人还是很迷惑:在一个单使用者的个人电脑上,多工有什么用呢?好了,最後事实表示即使是不知道这一概念的使用者也都需要多工的。 本文来自织梦

DOS下的多工
 

copyright dedecms

在最初PC上的Intel 8088微处理器并不是为多工而设计的。部分原因(我在上一章中讨论过)是记忆体管理不够强。当启动和结束多个程式时,多工的作业系统通常需要移动记忆体块以收集空闲记忆体。在8088上是不可能透明於应用系统来做到这一点的。

内容来自dedecms

DOS本身对多工没有太大的帮助,它的设计目的是尽可能小巧,并且与独立於应用程式之外,因此,除了载入程式以及对程式提供档案系统的存取功能,它几乎没有提供任何支援。

copyright dedecms

不过,有创意的程式写作者仍然在DOS的早期就找到了一种克服这些缺陷的方法,大多数是使用常驻(TSRterminate-and-stay-resident)程式。有些TSR,比如背景列印伫列程式等,透过拦截硬体时钟中断来执行真正的背景处理。其他的TSR,诸如SideKick等突现式工具,可以执行某种型态的工作切换-暂停目前的应用程式,执行突现式工具。DOS也逐渐有所增强以便提供对TSR的支援。 织梦好,好织梦

一些软体厂商试图在DOS之上架构出工作切换或者多工的外壳程式(shell)(诸如QuarterdeckDesqView),但是在这些环境中,仅有其中一个占据了大部分市场,当然,这就是Windows织梦内容管理系统

非优先权式的多工
 

本文来自织梦

Microsoft1985年发表Windows 1.0时,它是最成熟的解决方案,目的是突破DOS的局限。Windows在实际模式下执行。但是即使这样,它已可以在实体记忆体中移动记忆体块。这是多工的前提,虽然移动的方法尚未完全透明於应用程式,但是几乎可以忍受了。 织梦好,好织梦

在图形视窗环境中,多工比在一种命令列单使用者作业系统中显得更有意义。例如,在传统的命令列UNIX中,可以在命令列之外执行程式,让它们在背景执行。然而,程式的所有显示输出必须被重新转向到一个档案中,否则输出将和使用者正在做的事情混在一起。 内容来自dedecms

视窗环境允许多个程式在相同萤幕上一起执行,前後切换非常容易,并且还可以快速地将资料从一个程式移动到另一个程式中。例如,将绘图程式中建立的图片嵌入由文书处理程式编辑的文字档案中。在Windows中,以多种方式支援资料转移,首先是使用剪贴簿,後来又使用动态资料交换(DDE),而现在则是透过物件连结和嵌入(OLE)。 dedecms.com

不过,早期Windows的多工实作还不是多使用者作业系统中传统的优先权式的分时多工。这些作业系统使用系统时钟周期性地中断一个工作并开始另一个工作。Windows的这些16位元版本支援一种被称为「非优先权式的多工」,由於Windows讯息驱动的架构而使这种型态的多工成为可能。通常情况下,一个Windows程式将在记忆体中睡眠,直到它收到一个讯息为止。这些讯息通常是使用者的键盘或滑鼠输入的直接或间接结果。当处理完讯息之後,程式将控制权返回给Windows

织梦好,好织梦

Windows16位元版本不会绝对地依据一个timer tick将控制权从一个Windows程式切换到另一个,任何的工作切换都发生在当程式完成对讯息的处理後将控制权返回给Windows时。这种非优先权式的多工也被称为「合作式的多工」,因为它要求来自应用程式方面的一些合作。一个Windows程式可以占用整个系统,如果它要花很长一段时间来处理讯息的话。 copyright dedecms

虽然非优先权式的多工是16位元Windows的一般规则,但仍然出现了某些形式的优先权式多工。Windows使用优先权式多工来执行DOS程式,而且,为了实作多媒体,还允许动态连结程式库接收硬体时钟中断。

织梦好,好织梦

16位元Windows包括几个功能特性来帮助程式写作者解决(或者,至少可以说是对付)非优先权式多工中的局限,最显著的当然是时钟式滑鼠游标。当然,这并非一种解决方案,而仅仅是让使用者知道一个程式正在忙於处理一件冗长作业,因而让使用者在一段时间内无法使用系统。另一种解决方案是Windows计时器,它允许程式周期性地接收讯息并完成一些工作。计时器通常用於时钟应用和动画。 copyright dedecms

针对非优先权式多工的另一种解决方案是PeekMessage函式呼叫,我们曾在第五章中的RANDRECT程式里看到过。一个程式通常使用GetMessage呼叫从它的讯息伫列中找寻下一个讯息,不过,如果在讯息伫列中没有讯息,那么GetMessage不会传回,一直到出现一个讯息为止。而另一方面,PeekMessage将控制权传回程式,即使没有等待的讯息。这样,一个程式可以执行一个冗长作业,并在程式码中混入PeekMessage呼叫。只要没有这个程式或其他任何程式的讯息要处理,那么这个冗长作业将继续执行。

织梦好,好织梦

Presentation Manager和序列化的讯息伫列
  copyright dedecms

Microsoft在一种半DOS/Windows的环境下实作多工的第一个尝试(和IBM合作)是OS/2Presentation Manager(缩写成PM )。虽然OS/2明确地支援优先权式多工,但是这种多工方式似乎并未在Presentation Manager中得以落实。问题在於PM序列化来自键盘和滑鼠的使用者输入讯息。这意味著,在前一个使用者输入讯息被完全处理以前,PM不会将一个键盘或者滑鼠讯息传送给程式。

copyright dedecms

尽管键盘和滑鼠讯息只是一个PM(或者Windows)程式可以接收的许多讯息中的几个,大多数的其他讯息都是键盘或者滑鼠事件的结果。例如,功能表命令讯息是使用者使用键盘或者滑鼠进行功能表选择的结果。在处理功能表命令讯息时,键盘或者滑鼠讯息并未完全被处理。 本文来自织梦

序列化讯息伫列的主要原因是允许使用者的预先「键入」键盘按键和预先「按入」滑鼠按钮。如果一个键盘或者滑鼠讯息导致输入焦点从一个视窗切换到另一个视窗,那么接下来的键盘讯息应该进入拥有新的输入焦点的视窗中去。因此,系统不知道将下一个使用者输入讯息发送到何处,直到前一个讯息被处理完为止。 织梦好,好织梦

目前的共识是不应该让一个应用系统有可能占用整个系统,而这需要非序列化的讯息伫列,32位元版本的Windows支援这种讯息伫列。如果一个程式正在忙著处理一项冗长作业,那么您可以将输入焦点切换到另一个程式中。

dedecms.com

多执行绪解决方案
 

内容来自dedecms

我讨论OS/2Presentation Manager,只是因为它是第一个为早期的Windows程式写作者(比如我自己)介绍多执行绪的环境。有趣的是,PM实作多执行绪的局限为程式写作者提供了应该如何架构多执行绪程式的必要线索。即使这些限制在32位元的Windows中已经大幅减少,但是从更有限的环境中学到的经验仍然是非常有效的。因此,让我们继续讨论下去。 本文来自织梦

在一个多执行绪环境中,程式可以将它们自己分隔为同时执行的片段(叫做执行绪)。对执行绪的支援是解决PM中存在的序列化讯息伫列的最好方法,并且在Windows中执行绪有更实际的意义。

织梦好,好织梦

就程式码来说,一个执行绪简单地被表示为可能呼叫程式中其他函式的函式。程式从其主执行绪开始执行,这个主执行绪是在传统的C程式中叫做main的函式,而在Windows中是WinMain。一旦执行起来,程式可以通过在系统呼叫CreateThread中指定初始执行绪函式的名称来建立新的执行绪的执行。作业系统在执行绪之间优先权式地切换控制项,和它在程序之间切换控制权的方法非常类似。 织梦好,好织梦

OS/2Presentation Manager中,每个执行绪可以建立一个讯息伫列,也可以不建立。如果希望从执行绪建立视窗,那么一个PM执行绪必须建立讯息伫列。否则,如果只是进行许多的资料处理或者图形输出,那么执行绪不需要建立讯息伫列。因为无讯息伫列的程序不处理讯息,所以它们将不会当住系统。唯一的限制是一个无讯息伫列执行绪无法向一个讯息伫列执行绪中的视窗发送讯息,或者呼叫任何发送讯息的函式(不过,它们可以将讯息递送给讯息伫列执行绪)。 织梦内容管理系统

这样,PM程式写作者学会了如何将它们的程式分隔为一个讯息伫列执行绪(在其中建立所有的视窗并处理传送给视窗的讯息)和一个或者多个无讯息伫列执行绪,在其中执行冗长的背景工作。PM程式写作者还了解到「1/10秒规则」,大体上,程式写作者被告知,一个讯息伫列执行绪处理任何讯息都不应该超过1/10秒,任何花费更长时间的事情都应该在另一个执行绪中完成。如果所有的程式写作者都遵循这一规则,那么将没有PM程式会将系统当住超过1/10秒。 内容来自dedecms

多执行绪架构
 

dedecms.com

我已经说过PM的限制让程式写作者理解如何在图形环境中执行的程式里头使用多个执行绪提供了必要的线索。因此在这里我将为您的程式建议一种架构:您的主执行绪建立您程式所需要的所有视窗,并在其中包含所有的视窗讯息处理程式,以便处理这些视窗的所有讯息;所有其他执行绪只进行一些背景处理,除了和主执行绪通讯,它们不和使用者进行交流。 织梦内容管理系统

可以把这种架构想像成:主执行绪处理使用者输入(和其他讯息),并建立程序中的其他执行绪,这些附加的执行绪完成与使用者无关的工作。 织梦内容管理系统

换句话说,您程式的主执行绪是一个老板,而您的其他执行绪是老板的职员。老板将大的工作丢给职员处理,而他自己保持和外界的联系。因为那些执行绪仅仅是职员,所以其他执行绪不会举行它们自己的记者招待会。它们会认真地完成自己的工作,将结果报告给老板,并等待他们的下一个任务。

copyright dedecms

一个程式中的执行绪是同一程序的不同部分,因此他们共用程序的资源,如记忆体和打开的档案。因为执行绪共用程式的记忆体,所以他们还共用静态变数。然而,每个执行绪都有他们自己的堆叠,因此动态变数对每个执行绪是唯一的。每个执行绪还有各自的处理器状态(和数学辅助运算器状态),这个状态在进行执行绪切换期间被储存和恢复。

copyright dedecms

执行绪间的「争吵」
  织梦内容管理系统

正确地设计、写作和测试一个复杂的多执行绪应用程式显然是Windows程式写作者可能遇到的最困难的工作之一。因为优先权式多工系统可以在任何时刻中断一个执行绪,并将控制权切换到另一个执行绪中,在两个执行绪之间可能有无法预料的随机交互作用的情况。

copyright dedecms

多执行绪程式中的一个常见的错误被称为「竞争状态(race condition)」,这发生在程式写作者假设一个执行绪在另一个执行绪需要某资料之前已经完成了某些处理(如准备资料)的时候。为了帮助协调执行绪的活动,作业系统要求各种形式的同步。一种是同步信号(semaphore),它允许程式写作者在程式码中的某一点阻止一个执行绪的执行,直到另一个执行绪发信号让它继续为止。类似於同步信号的是「临界区域(critical section)」,它是程式码中不可中断的部分。 copyright dedecms

但是同步信号还可能产生称为「锁死(deadlock)」的常见执行绪错误,这发生在两个执行绪互相阻止了另一个的执行,而继续执行的唯一办法又是它们继续向前执行。

dedecms.com

幸运的是,32位元程式比16位元程式更能抵抗执行绪所涉及的某些问题。例如,假定一个执行绪执行下面的简单叙述:

dedecms.com

lCount++ ; 本文来自织梦 

其中lCount是由其他执行绪使用的一个32位元的long型态变数,C中的这个叙述被编译为两条机械码指令,第一条将变数的低16位元加1,而第二条指令将任何可能的进位加到高16位上。假定作业系统在这两个机械码指令之间中断了执行绪。如果lCount在第一条机械码指令之前是0x0000FFFF,那么lCount在执行绪被中断时为0,而这正是另一个执行绪将看到的值。只有当执行绪继续执行时,lCount才会增加到正确的值0x00010000

dedecms.com

这是那些偶尔会导致操作问题的错误之一。在16位元程式中,解决此问题正确的方法是将叙述包含在一个临界区域中,在这期间执行绪不会被中断。然而,在一个32位元程式中,该叙述是正确的,因为它被编译为一条机械码指令。

内容来自dedecms

Windows的好处
 

织梦好,好织梦

32位元Windows版本(包括Windows NTWindows 98)有一个非序列化的讯息伫列。这种实作似乎非常好:如果一个程式正在花费一段长时间处理一个讯息,那么滑鼠位於该程式的视窗上时,滑鼠游标将呈现为一个时钟,但是当将滑鼠移到另一个程式的视窗上时,滑鼠游标将变为正常的箭头形状。只需按一下就可以将另一个视窗提到前面来。

dedecms.com

然而,使用者仍然不能使用正在处理大量工作的那个程式,因为那些工作会阻止程式接收其他讯息,这不是我们所希望的。一个程式应该总是能随时处理讯息的,所以这时就需要使用从属执行绪了。 dedecms.com

Windows NTWindows 98中,没有讯息伫列执行绪和无讯息伫列执行绪的区别,每个执行绪在建立时都会有它自己的讯息伫列,从而减少了PM程式中关於执行绪的一些不便规定(然而,在大多数情况下,您仍然想通过一条专门处理讯息的执行绪中的讯息程序处理输入,而将冗长作业交给那些不包含视窗的执行绪处理,这种结构几乎总是最容易理解的,我们将看到这一点)。

内容来自dedecms

还有更好的事情:Windows NTWindows 98中有个函式允许执行绪杀死同一程序中的另一个执行绪。当您开始编写多执行绪程式码时,您将会发现这种功能在有时是很方便的。OS/2的早期版本没有「杀死执行绪」的函式。 copyright dedecms

最後的好讯息(至少对这里的话题是好讯息)是Windows NTWindows 98实作了一些被称为「执行绪区域储存空间(TLSthread local storage)」的功能。为了了解这一点,回顾一下我在前面提到过的,静态变数(对一个函式来说,既是整体又是区域变数)在执行绪之间是被共用的,因为它们位於程序的资料储存空间中。动态变数(对一个函式来说总是区域变数)对每一个执行绪则是唯一的,因为它们占据堆叠上的空间,而每个执行绪都有它自己的堆叠。

织梦内容管理系统

有时让两个或多个执行绪使用相同的函式,而让这些执行绪使用唯一於执行绪的静态变数,那会带来很大便利。这就是执行绪区域储存空间,其中涉及一些Windows函式呼叫,但是Microsoft还为C编译器进行扩展,使执行绪区域储存空间的使用更透明于程式写作者。 内容来自dedecms

新改良过的!支援多执行绪了!
  dedecms.com

既然已经介绍了执行绪的现状,让我们来展望一下执行绪的未来。有时,有人会出现一种使用作业系统所提供的每一种功能特性的冲动。最坏的情况是,当您的老板走到您的桌前并说:「我听说这种新功能非常炫,让我们在自己的程式中用一些这种新功能吧。」然後您将花费一个星期的时间,试图去了解您的应用程式如何从这种新功能获益。

copyright dedecms

应该注意的是,在并不需要多执行绪的应用系统中加入多执行绪是没有任何意义的。如果您的程式显示沙漏游标的时间太长,或者如果它使用PeekMessage呼叫来避免沙漏游标的出现,那么请重新规划您的程式架构,使用多执行绪可能会是一个好主意。其他情形,您是在为难您自己,并可能会在程式码中产生新的错误。 内容来自dedecms

在某些情况下,沙漏游标的出现可能是完全适当的。我在前面提到过「1/10秒规则」,而将一个大档案载入记忆体可能会花费多於1/10秒的时间,这是否意味著档案载入常式应该在分离的执行绪中实作呢?没有必要。当使用者命令一个程式打开档案时,他或者她通常想立即完成该操作。将档案载入常式放在分离的执行绪中只会增加额外的负担。即使您想向您的朋友夸耀您在编写多执行绪程式,也完全不值得这样做! copyright dedecms

WINDOWS的多执行绪处理
 

本文来自织梦

建立新的执行绪的API函式是CreateThread,它的语法如下: copyright dedecms

hThread = CreateThread (&security_attributes, dwStackSize, ThreadProc,  织梦内容管理系统 
                        pParam, dwFlags, &idThread) ; dedecms.com 

第一个参数是指向SECURITY_ATTRIBUTES型态的结构的指标。在Windows 98中忽略该参数。在Windows NT中,它被设为NULL。第二个参数是用於新执行绪的初始堆叠大小,预设值为0。在任何情况下,Windows根据需要动态延长堆叠的大小。

内容来自dedecms

CreateThread的第三个参数是指向执行绪函式的指标。函式名称没有限制,但是必须以下列形式宣告: dedecms.com

DWORD WINAPI ThreadProc (PVOID pParam) ; 
织梦内容管理系统

CreateThread的第四个参数为传递给ThreadProc的参数。这样主执行绪和从属执行绪就可以共用资料。 织梦内容管理系统

CreateThread的第五个参数通常为0,但当建立的执行绪不马上执行时为旗标CREATE_SUSPENDED。执行绪将暂停直到呼叫ResumeThread来恢复执行绪的执行为止。第六个参数是一个指标,指向接受执行绪ID值的变数。 本文来自织梦

大多数Windows程式写作者喜欢用在PROCESS.H表头档案中宣告的C执行时期程式库函式_beginthread。它的语法如下:

织梦内容管理系统

hThread = _beginthread (ThreadProc, uiStackSize, pParam) ; 
内容来自dedecms

它更简单,对於大多数应用程式很完美,这个执行绪函式的语法为:

织梦好,好织梦

void __cdecl ThreadProc (void * pParam) ; 织梦好,好织梦 

再论随机矩形
 

内容来自dedecms

程式20-1 RNDRCTMT是第五章里的RANDRECT程式的多执行绪版本,您将回忆起RANDRECT使用的是PeekMessage回圈来显示一系列的随机矩形。

copyright dedecms

 程式20-1  RNDRCTMT 

本文来自织梦

RNDRCTMT.C dedecms.com 
/*--------------------------------------------------------------------------- 

本文来自织梦

        RNDRCTMT.C -- Displays Random Rectangles 
内容来自dedecms
                                                             (c) Charles Petzold, 1998 
织梦好,好织梦
  -------------------------------------------------------------------------*/ 织梦好,好织梦 
  织梦内容管理系统 
#include <windows.h> 
内容来自dedecms
#include <process.h> 织梦好,好织梦 
  织梦内容管理系统 
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; 织梦好,好织梦 
HWND    hwnd ; 
织梦好,好织梦
int     cxClient, cyClient ; 

织梦好,好织梦

  
dedecms.com
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, 内容来自dedecms 
                    PSTR szCmdLine, int iCmdShow) 织梦内容管理系统 
{ 

内容来自dedecms

        static TCHAR   szAppName[] = TEXT ("RndRctMT") ; 

织梦好,好织梦

        MSG            msg ; 

织梦内容管理系统

        WNDCLASS       wndclass ; dedecms.com 
      copyright dedecms 
        wndclass.style                                = CS_HREDRAW | CS_VREDRAW ; 织梦好,好织梦 
        wndclass.lpfnWndProc                          = WndProc ; 
织梦好,好织梦
        wndclass.cbClsExtra                           = 0 ; 
dedecms.com
        wndclass.cbWndExtra                           = 0 ; 

本文来自织梦

        wndclass.hInstance                            = hInstance ; 
织梦好,好织梦
        wndclass.hIcon                                = LoadIcon (NULL, IDI_APPLICATION) ; 内容来自dedecms 
        wndclass.hCursor                              = LoadCursor (NULL, IDC_ARROW) ; 

本文来自织梦

        wndclass.hbrBackground                = (HBRUSH) GetStockObject (WHITE_BRUSH) ; 

织梦内容管理系统

        wndclass.lpszMenuName                 = NULL ; copyright dedecms 
        wndclass.lpszClassName                = szAppName ; 
copyright dedecms
         

copyright dedecms

        if (!RegisterClass (&wndclass)) 织梦内容管理系统 
        { dedecms.com 
        MessageBox (   NULL, TEXT ("This program requires Windows NT!"), 

内容来自dedecms

                      szAppName, MB_ICONERROR) ; 

copyright dedecms

                       return 0 ; 
织梦内容管理系统
     } 本文来自织梦 
        hwnd = CreateWindow (  szAppName, TEXT ("Random Rectangles"), 本文来自织梦 
                               WS_OVERLAPPEDWINDOW, 织梦好,好织梦 
                               CW_USEDEFAULT, CW_USEDEFAULT, 

内容来自dedecms

                               CW_USEDEFAULT, CW_USEDEFAULT, copyright dedecms 
                               NULL, NULL, hInstance, NULL) ; dedecms.com 
      copyright dedecms 
        ShowWindow (hwnd, iCmdShow) ; 织梦好,好织梦 
        UpdateWindow (hwnd) ; 
copyright dedecms
      copyright dedecms 
        while (GetMessage (&msg, NULL, 0, 0)) 

织梦好,好织梦

        { 织梦好,好织梦 
                       TranslateMessage (&msg) ; 本文来自织梦 
                       DispatchMessage (&msg) ; 
dedecms.com
        } 内容来自dedecms 
        return msg.wParam ; 

本文来自织梦

} 

dedecms.com

  

本文来自织梦

VOID Thread (PVOID pvoid) 
内容来自dedecms
{ dedecms.com 
        HBRUSH hBrush ; 

内容来自dedecms

        HDC            hdc ; copyright dedecms 
        int            xLeft, xRight, yTop, yBottom, iRed, iGreen, iBlue ; 织梦好,好织梦 
      

本文来自织梦

        while (TRUE) 织梦内容管理系统 
     { 本文来自织梦 
                       if (cxClient != 0 || cyClient != 0) 

dedecms.com

                       { 
织梦内容管理系统
                              xLeft                  = rand () % cxClient ; 织梦内容管理系统 
                               xRight                 = rand () % cxClient ; 内容来自dedecms 
                               yTop           = rand () % cyClient ; dedecms.com 
                               yBottom        = rand () % cyClient ; 本文来自织梦 
                               iRed                   = rand () & 255 ; 

copyright dedecms

                               iGreen                 = rand () & 255 ; 织梦内容管理系统 
                               iBlue                  = rand () & 255 ; 
copyright dedecms
                
内容来自dedecms
                       hdc = GetDC (hwnd) ; 
内容来自dedecms
                       hBrush = CreateSolidBrush (RGB (iRed, iGreen, iBlue)) ; copyright dedecms 
                       SelectObject (hdc, hBrush) ; 
dedecms.com
                
本文来自织梦
               Rectangle (hdc,min (xLeft, xRight), min (yTop, yBottom), 织梦内容管理系统 
                max (xLeft, xRight), max (yTop, yBottom)) ; 
内容来自dedecms
                织梦好,好织梦 
                                      ReleaseDC (hwnd, hdc) ; 
织梦内容管理系统
                                      DeleteObject (hBrush) ; 
copyright dedecms
                               } 

织梦好,好织梦

        } copyright dedecms 
} 内容来自dedecms 
LRESULT CALLBACK WndProc (     HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 
织梦好,好织梦
{ 

dedecms.com

        switch (message) 

织梦内容管理系统

        { 
本文来自织梦
        case WM_CREATE: 

内容来自dedecms

                               _beginthread (Thread, 0, NULL) ; 

织梦内容管理系统

                               return 0 ; 
dedecms.com
           织梦好,好织梦 
        case    WM_SIZE: 织梦好,好织梦 
                               cxClient = LOWORD (lParam) ; 

内容来自dedecms

                               cyClient = HIWORD (lParam) ; 本文来自织梦 
                               return 0 ; 

copyright dedecms

           

dedecms.com

        case    WM_DESTROY: copyright dedecms 
                               PostQuitMessage (0) ; 
本文来自织梦
                               return 0 ; 

织梦好,好织梦

        } 
dedecms.com
        return DefWindowProc (hwnd, message, wParam, lParam) ; dedecms.com 
} 
织梦好,好织梦

在建立多执行绪的Windows程式时,需要在「Project Settings」对话方块中做一些修改。选择「C/C++」页面标签,然後在「Category」下拉式清单方块中选择「Code Generation」。在「Use Run-Time Library」下拉式清单方块中,可以看到用於「Release」设定的「Single-Threaded」和用於Debug设定的「Debug Single-Threaded」。将这些分别改为「Multithreaded」和「Debug Multithreaded」。这将把编译器旗标改为/MT,它是编译器在编译多执行绪的应用程式所需要的。具体地说,编译器将在.OBJ档案中插入LIBCMT.LIB档案名,而不是LIBC.LIB。连结程式使用这个名称与执行期程式库函式连结。 dedecms.com

LIBC.LIBLIBCMT.LIB档案包含C语言程式库函式,有些C语言程式库函式包含静态资料。例如,由於strtok函式可能被连续地多次呼叫,所以它在静态记忆体中储存了一个指标。在多执行绪程式中,每个执行绪必须在strtok函式中有它自己的静态指标。因此,这个函式的多执行绪版本稍微不同於单执行绪的strtok函式。

dedecms.com

同时请注意,我在RNDRCTMT.C中包含了表头档案PROCESS.H,这个档案定义一个名为_beginthread的函式,它启动一个新的执行绪。只有定义了_MT识别字,才会宣告这个函式,这是/MT旗标的另一个结果。

内容来自dedecms

RNDRCTMT.CWinMain函式中,由CreateWindow传回的hwnd值被储存在一个整体变数中,因此cxClientcyClient值也可以由视窗讯息处理程式的WM_SIZE讯息获得。

内容来自dedecms

视窗讯息处理程式以最容易的方法呼叫_beginthread-简单地以执行绪函式的位址(称为Thread)作为第一个参数,其他参数使用0,执行绪函式传回VOID并有一个参数,该参数是一个指向VOID的指标。在RNDRCTMT中的Thread函式不使用这个参数。

织梦内容管理系统

在呼叫了_beginthread函式之後,执行绪函式(以及该执行绪函式可能呼叫的其他任何函式)中的程式码和程式中的其他程式码同时执行。两个或者多个执行绪使用一个程序中的同一函式,在这种情况下,动态区域变数(储存在堆叠上)对每个执行绪是唯一的。对程序中的所有执行绪来说,所有的静态变数都是一样的。这就是视窗讯息处理程式设定整体的cxClientcyClient变数并由Thread函式使用的方式。 内容来自dedecms

有时您需要唯一於各个执行绪的持续储存性资料。通常,这种资料是静态变数,但在Windows 98中,您可以使用「执行绪区域储存空间」,我将在本章後面进行讨论。

内容来自dedecms

程式设计竞赛的问题
 

织梦内容管理系统

1986103日,Microsoft举行了为期一天,针对电脑杂志出版社的技术编辑和作者的简短的记者招待会,来讨论他们当时的一组语言产品,包括他们的第一个交谈式开发环境,QuickBASIC 2.0。当时,Windows 1.0出现还不到一年,但是没有人知道我们什么时候能得到与该环境类似的东西(这花了好几年)。这一事件与众不同的部分原因是由於Microsoft的公关人员所举办的「Storm the Gates」程式设计竞赛。Bill Gates使用QuickBASIC 2.0,而电脑出版社的人员可以使用他们选择的任何语言产品。

织梦好,好织梦

竞赛的问题是从公众提出的题目中挑选出来的(挑选那些需要写大约半小时程式来解决的问题),问题如下: 内容来自dedecms

建立一个包含四个视窗的多工模拟程式。第一个视窗必须显示一系列的递增数,第二个必须显示一系列的递增质数,而第三个必须显示Fibonacci数列(Fibonacci数列以数字01开始,後头每一个数都是其前两个数的和-即0112358等等)。这三个视窗应该在数字达到视窗底部时或者进行滚动,或者自行清除视窗内容。第四个视窗必须显示任意半径的圆,而程式必须在按下一个Escape键时终止。

dedecms.com

当然,在198610月,在DOS下执行的这样一个程式最多只能是模拟多工而已,而且没有一个竞赛者具有足够的勇气-并且其中大多数也没有足够的知识-来为Windows编写这个程式。再者,如果真要这么做,当然不会只花半小时了!

织梦内容管理系统

参加这次竞赛的大多数人编写了一个程式来将萤幕分为四个区域,程式中包含一个回圈,依次更新每个视窗,然後检查是否按下了Escape键。如同DOS环境下的传统习惯,程式占用了百分之百的CPU处理时间。 织梦内容管理系统

如果在Windows 1.0中写程式,那么结果将是类似程式20-2 MULTI1的结果。我说「类似」,是因为我编写的程式是32位元的,但程式结构和相当多的程式码-除了变数和函式参数定义以及Unicode支援-都是相同的。 内容来自dedecms

 程式20-2  MULTI1 织梦内容管理系统 
MULTI1.C 
织梦内容管理系统
/*-------------------------------------------------------------------------- 

织梦内容管理系统

        MULTI1.C --    Multitasking Demo 织梦内容管理系统 
                                                     (c) Charles Petzold, 1998 

本文来自织梦

----------------------------------------------------------------------------*/ 

copyright dedecms

  
内容来自dedecms
#include <windows.h> 
copyright dedecms
#include <math.h> 
织梦好,好织梦
  

织梦好,好织梦

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; 
copyright dedecms
int cyChar ; dedecms.com 
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, 
织梦内容管理系统
                  PSTR szCmdLine, int iCmdShow) 

织梦内容管理系统

{ 

copyright dedecms

        static TCHAR   szAppName[] = TEXT ("Multi1") ; 本文来自织梦 
        HWND                                  hwnd ; 

dedecms.com

        MSG                                   msg ; dedecms.com 
        WNDCLASS                       wndclass ; 织梦好,好织梦 
      

copyright dedecms

        wndclass.style                        = CS_HREDRAW | CS_VREDRAW ; 

织梦内容管理系统

        wndclass.lpfnWndProc                  = WndProc ; 
织梦好,好织梦
        wndclass.cbClsExtra                   = 0 ; 

织梦内容管理系统

        wndclass.cbWndExtra                   = 0 ; 织梦好,好织梦 
        wndclass.hInstance                    = hInstance ; 本文来自织梦 
        wndclass.hIcon                        = LoadIcon (NULL, IDI_APPLICATION) ; copyright dedecms 
        wndclass.hCursor                      = LoadCursor (NULL, IDC_ARROW) ; 织梦内容管理系统 
        wndclass.hbrBackground         = (HBRUSH) GetStockObject (WHITE_BRUSH) ; 
织梦内容管理系统
        wndclass.lpszMenuName          = NULL ; copyright dedecms 
        wndclass.lpszClassName         = szAppName ; 

copyright dedecms

      织梦内容管理系统 
        if (!RegisterClass (&wndclass)) 

本文来自织梦

        { copyright dedecms 
        MessageBox (NULL, TEXT ("This program requires Windows NT!"), 本文来自织梦 
                    szAppName, MB_ICONERROR) ; 织梦好,好织梦 
                       return 0 ; 本文来自织梦 
     } 
织梦内容管理系统
     hwnd = CreateWindow (     szAppName, TEXT ("Multitasking Demo"), 

织梦好,好织梦

                               WS_OVERLAPPEDWINDOW, 织梦内容管理系统 
                               CW_USEDEFAULT, CW_USEDEFAULT, 

织梦好,好织梦

                               CW_USEDEFAULT, CW_USEDEFAULT, 织梦好,好织梦 
                               NULL, NULL, hInstance, NULL) ; 
内容来自dedecms
      
copyright dedecms
        ShowWindow (hwnd, iCmdShow) ; 

dedecms.com

        UpdateWindow (hwnd) ; 内容来自dedecms 
      
织梦内容管理系统
        while (GetMessage (&msg, NULL, 0, 0)) 

copyright dedecms

        { 
copyright dedecms
                       TranslateMessage (&msg) ; 
本文来自织梦
                       DispatchMessage (&msg) ; 织梦好,好织梦 
        } 
织梦内容管理系统
        return msg.wParam ; dedecms.com 
} 
dedecms.com
  
织梦好,好织梦
int CheckBottom (HWND hwnd, int cyClient, int iLine) 织梦好,好织梦 
{ 
内容来自dedecms
        if (iLine * cyChar + cyChar > cyClient) 

织梦好,好织梦

        { 

内容来自dedecms

                       InvalidateRect (hwnd, NULL, TRUE) ; 本文来自织梦 
                       UpdateWindow (hwnd) ; copyright dedecms 
                       iLine = 0 ; 织梦好,好织梦 
     } 内容来自dedecms 
        return iLine ; 织梦内容管理系统 
} 

织梦内容管理系统

  

dedecms.com

// ------------------------------------------------------------------------- 织梦内容管理系统 
// Window 1: Display increasing sequence of numbers 

织梦好,好织梦

// ------------------------------------------------------------------------- 

织梦好,好织梦

  
dedecms.com
LRESULT APIENTRY WndProc1 (    HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 
内容来自dedecms
{ 织梦内容管理系统 
        static int            iNum, iLine, cyClient ; 
织梦内容管理系统
        HDC                                   hdc ; 
dedecms.com
        TCHAR                                 szBuffer[16] ; copyright dedecms 
      本文来自织梦 
        switch (message) 织梦内容管理系统 
        { 织梦好,好织梦 
        case    WM_SIZE: 本文来自织梦 
                               cyClient = HIWORD (lParam) ; 

本文来自织梦

                               return 0 ; 
织梦内容管理系统
  copyright dedecms 
        case    WM_TIMER: 本文来自织梦 
                               if (iNum < 0) 本文来自织梦 
                                              iNum = 0 ; 

织梦好,好织梦

  
内容来自dedecms
                               iLine = CheckBottom (hwnd, cyClient, iLine) ; 本文来自织梦 
                               hdc = GetDC (hwnd) ; dedecms.com 
  
copyright dedecms
                               TextOut (hdc, 0, iLine * cyChar, szBuffer,  
织梦好,好织梦
                                                             wsprintf (szBuffer, TEXT ("%d"), iNum++)) ; 本文来自织梦 
  

copyright dedecms

                               ReleaseDC (hwnd, hdc) ; 

dedecms.com

                               iLine++ ; 
织梦好,好织梦
                               return 0 ; 
织梦内容管理系统
        } 

dedecms.com

        return DefWindowProc (hwnd, message, wParam, lParam) ; 
织梦好,好织梦
} 织梦好,好织梦 
  本文来自织梦 
// -------------------------------------------------------------------------- 
本文来自织梦
// Window 2: Display increasing sequence of prime numbers 

织梦好,好织梦

// -------------------------------------------------------------------------- dedecms.com 
  

本文来自织梦

LRESULT APIENTRY WndProc2 (    HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) copyright dedecms 
{ copyright dedecms 
        static int             iNum = 1, iLine, cyClient ; 
copyright dedecms
        HDC                                   hdc ; 

内容来自dedecms

        int                                   i, iSqrt ; 

dedecms.com

        TCHAR                                 szBuffer[16] ; 织梦好,好织梦 
      
织梦好,好织梦
        switch (message) 内容来自dedecms 
        { 本文来自织梦 
        case    WM_SIZE: 本文来自织梦 
                               cyClient = HIWORD (lParam) ; 
本文来自织梦
                               return 0 ; 内容来自dedecms 
           
织梦内容管理系统
        case    WM_TIMER: 内容来自dedecms 
                               do             { 织梦内容管理系统 
                                              if (++iNum < 0) 

dedecms.com

                                                                     iNum = 0 ; 织梦内容管理系统 
                织梦好,好织梦 
                                              iSqrt = (int) sqrt (iNum) ; 织梦内容管理系统 
  
内容来自dedecms
                                              for (i = 2 ; i <= iSqrt ; i++) 

内容来自dedecms

                                                                     if (iNum % i == 0) copyright dedecms 
                                                                                            break ; 

copyright dedecms

                               } 织梦好,好织梦 
                               while (i <= iSqrt) ; 织梦内容管理系统 
           
copyright dedecms
                               iLine = CheckBottom (hwnd, cyClient, iLine) ; 织梦内容管理系统 
                               hdc = GetDC (hwnd) ; 

copyright dedecms

  dedecms.com 
                               TextOut (      hdc, 0, iLine * cyChar, szBuffer,  
copyright dedecms
                                                                     wsprintf (szBuffer, TEXT ("%d"), iNum)) ; 内容来自dedecms 
                               ReleaseDC (hwnd, hdc) ; 

内容来自dedecms

                               iLine++ ; 

本文来自织梦

                               return 0 ; 
织梦内容管理系统
        } dedecms.com 
        return DefWindowProc (hwnd, message, wParam, lParam) ; 
内容来自dedecms
} 

织梦好,好织梦

  
织梦内容管理系统
// -------------------------------------------------------------------------- 
织梦内容管理系统
// Window 3: Display increasing sequence of Fibonacci numbers 
内容来自dedecms
// -------------------------------------------------------------------------- 
织梦内容管理系统
  copyright dedecms 
LRESULT APIENTRY WndProc3 (    HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam) 

织梦内容管理系统

{ 织梦好,好织梦 
        static int             iNum = 0, iNext = 1, iLine, cyClient ; 
dedecms.com
        HDC                                   hdc ; 内容来自dedecms 
        int                                   iTemp ; 
copyright dedecms
        TCHAR                                 szBuffer[16] ; 
织梦内容管理系统
      织梦好,好织梦 
        switch (message) 织梦好,好织梦 
        { 织梦好,好织梦 
        case WM_SIZE: copyright dedecms 
                               cyClient = HIWORD (lParam) ; 
dedecms.com
                               return 0 ; 

内容来自dedecms

           
dedecms.com
        case    WM_TIMER: 
copyright dedecms
                               if (iNum < 0) 本文来自织梦 
                               { dedecms.com 
                                      iNum  = 0 ; 
dedecms.com
                                      iNext = 1 ; 

copyright dedecms

                               } copyright dedecms 
           织梦内容管理系统 
                               iLine = CheckBottom (hwnd, cyClient, iLine) ; 织梦内容管理系统 
                               hdc = GetDC (hwnd) ; 

织梦内容管理系统

  dedecms.com 
                               TextOut (      hdc, 0, iLine * cyChar, szBuffer,  
本文来自织梦
                                                                     wsprintf (szBuffer, "%d", iNum)) ; 
本文来自织梦
                               ReleaseDC (hwnd, hdc) ; 

内容来自dedecms

                               iTemp          =       iNum ; 
内容来自dedecms
                               iNum           =       iNext ; 本文来自织梦 
                               iNex           +=      iTemp ; dedecms.com 
                               iLine++ ; 本文来自织梦 
                               return 0 ; 

copyright dedecms

        } 
dedecms.com
        return DefWindowProc (hwnd, message, wParam, lParam) ; 内容来自dedecms 
} 织梦好,好织梦 
  

内容来自dedecms

// -------------------------------------------------------------------------- 本文来自织梦 
// Window 4: Display circles of random radii 织梦内容管理系统 
// --------------------------------------------------------------------------- 

织梦内容管理系统

  
copyright dedecms
LRESULT APIENTRY WndProc4 (    HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 

织梦内容管理系统

{ 
本文来自织梦
        static int             cxClient, cyClient ; 本文来自织梦 
        HDC                                   hdc ; 本文来自织梦 
        int                                   iDiameter ; 本文来自织梦 
      
内容来自dedecms
        switch (message) 织梦好,好织梦 
        { 
织梦内容管理系统
        case    WM_SIZE: 内容来自dedecms 
                               cxClient = LOWORD (lParam) ; 内容来自dedecms 
                               cyClient = HIWORD (lParam) ; 
织梦好,好织梦
                               return 0 ; 本文来自织梦 
           

织梦内容管理系统

        case    WM_TIMER: 织梦内容管理系统 
                               InvalidateRect (hwnd, NULL, TRUE) ; 
copyright dedecms
                               UpdateWindow (hwnd) ; 

copyright dedecms

           内容来自dedecms 
                               iDiameter = rand() % (max (1, min (cxClient, cyClient))) ; 

dedecms.com

                               hdc = GetDC (hwnd) ; 织梦内容管理系统 
           织梦内容管理系统 
                               Ellipse (hdc,          (cxClient - iDiameter) / 2, 
copyright dedecms
                                              (cyClient - iDiameter) / 2, copyright dedecms 
                                              (cxClient + iDiameter) / 2, 本文来自织梦 
                                              (cyClient + iDiameter) / 2) ; 
内容来自dedecms
           织梦内容管理系统 
                               ReleaseDC (hwnd, hdc) ; 本文来自织梦 
                               return 0 ; 
织梦好,好织梦
        } 内容来自dedecms 
        return DefWindowProc (hwnd, message, wParam, lParam) ; 本文来自织梦 
} 
织梦好,好织梦
// -------------------------------------------------------------------------- 
dedecms.com
// Main window to create child windows 

织梦好,好织梦

// -------------------------------------------------------------------------- 织梦好,好织梦 
  
织梦好,好织梦
LRESULT APIENTRY WndProc (     HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 

内容来自dedecms

{ 织梦内容管理系统 
        static HWND            hwndChild[4] ; copyright dedecms 
        static TCHAR * szChildClass[] =       { TEXT ("Child1"), TEXT ("Child2"), 织梦好,好织梦 
        TEXT ("Child3"), TEXT ("Child4") } ; 内容来自dedecms 
        static WNDPROC         ChildProc[] = { WndProc1, WndProc2, WndProc3, WndProc4 } ; 内容来自dedecms 
        HINSTANCE                      hInstance ; 织梦好,好织梦 
        int                                           i, cxClient, cyClient ; 
内容来自dedecms
        WNDCLASS                              wndclass ; 本文来自织梦 
      

织梦好,好织梦

        switch (message) copyright dedecms 
        { 

内容来自dedecms

        case    WM_CREATE: 

织梦内容管理系统

                               hInstance = (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE) ; 
dedecms.com
           
织梦好,好织梦
                               wndclass.style                                        = CS_HREDRAW | CS_VREDRAW ; 织梦好,好织梦 
                               wndclass.cbClsExtra                   = 0 ; 
内容来自dedecms
                               wndclass.cbWndExtra                   = 0 ; 

织梦内容管理系统

                               wndclass.hInstance                    = hInstance ; dedecms.com 
                               wndclass.hIcon                        = NULL ; 内容来自dedecms 
                               wndclass.hCursor                      = LoadCursor (NULL, IDC_ARROW) ; 织梦内容管理系统 
                               wndclass.hbrBackground         = (HBRUSH) GetStockObject (WHITE_BRUSH) ; 
copyright dedecms
                               wndclass.lpszMenuName          = NULL ; 
copyright dedecms
           dedecms.com 
                               for (i = 0 ; i < 4 ; i++) 
copyright dedecms
                               { 

织梦内容管理系统

                                              wndclass.lpfnWndProc   = ChildProc[i] ; 

dedecms.com

                                              wndclass.lpszClassName = szChildClass[i] ; dedecms.com 
                
织梦内容管理系统
                                              RegisterClass (&wndclass) ; 
织梦好,好织梦
                

织梦内容管理系统

                                              hwndChild[i] = CreateWindow (szChildClass[i], NULL, 

dedecms.com

                                WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE, 

内容来自dedecms

                                0, 0, 0, 0,  dedecms.com 
                                hwnd, (HMENU) i, hInstance, NULL) ; 

织梦内容管理系统

                               } copyright dedecms 
  

dedecms.com

                               cyChar = HIWORD (GetDialogBaseUnits ()) ; 内容来自dedecms 
                               SetTimer (hwnd, 1, 10, NULL) ; 

copyright dedecms

                               return 0 ; 

内容来自dedecms

           本文来自织梦 
        case    WM_SIZE: 
织梦好,好织梦
                               cxClient = LOWORD (lParam) ; 

织梦好,好织梦

                               cyClient = HIWORD (lParam) ; 

dedecms.com

           
织梦内容管理系统
                               for (i = 0 ; i < 4 ; i++) copyright dedecms 
               MoveWindow (hwndChild[i],      (i % 2) * cxClient / 2, 
织梦好,好织梦
                                            (i > 1) * cyClient / 2, 

本文来自织梦

                          cxClient / 2, cyClient / 2, TRUE) ; 

dedecms.com

                                      return 0 ; 
本文来自织梦
           
织梦好,好织梦
        case    WM_TIMER: 

织梦好,好织梦

                               for (i = 0 ; i < 4 ; i++) 
织梦好,好织梦
                                      SendMessage (hwndChild[i], WM_TIMER, wParam, lParam) ; copyright dedecms 
           内容来自dedecms 
                               return 0 ; copyright dedecms 
           

本文来自织梦

        case    WM_CHAR: dedecms.com 
                               if (wParam == '\x1B') copyright dedecms 
                                              DestroyWindow (hwnd) ; 织梦好,好织梦 
           本文来自织梦 
                               return 0 ; 

内容来自dedecms

           织梦好,好织梦 
        case    WM_DESTROY: copyright dedecms 
                               KillTimer (hwnd, 1) ; 织梦好,好织梦 
                               PostQuitMessage (0) ; 织梦内容管理系统 
                               return 0 ; 
本文来自织梦
       } 织梦好,好织梦 
        return DefWindowProc (hwnd, message, wParam, lParam) ; 织梦好,好织梦 
} 
copyright dedecms

在这个程式里实际上没有什么我们没见过的东西。主视窗建立四个子视窗,每个子视窗占据显示区域的一个象限。主视窗还设定一个Windows计时器并发送WM_TIMER讯息给四个子视窗中的每一个。 织梦内容管理系统

通常一个Windows程式应该保留足够的资讯以便在WM_PAINT讯息处理期间重建其视窗中的内容。MULTI1没有这么做,既然它绘制和清除视窗的速度如此之快,所以我认为那是不必要的。 内容来自dedecms

WndProc2中的质数产生器的效率并不很高,但是有效。如果一个数除了1和它自身以外没有别的因数,那么这个数就是质数。当然,要检查一个数是否是质数并不要求使用小於被检查数的所有数来除这个数并检查余数,而只需使用所有小於被检查数的平方根的数。平方根计算是发表浮点数的原因,否则,该程式将是完全依据整数的程式。 织梦内容管理系统

MULTI1程式没有什么不好的地方。使用Windows计时器是在Windows的早期(和目前)版本中模拟多工的一种好方法,然而,计时器的使用有时限制了程式的速度。如果程式可以在WM_TIMER讯息处理中更新它的所有视窗而还有时间剩余下来的话,那就意味著它并没有充分利用我们的机器资源。

copyright dedecms

一种可能的解决方案是在单个WM_TIMER讯息处理期间进行两次或者更多次的更新,但是到底多少次呢?这不得不依赖於机器的速度,而有很大的变动性。您当然不会想编写一个只能适用於25MHz38650MHz486100-GHzPentium VII上的程式吧。 copyright dedecms

多执行绪解决方案
 

内容来自dedecms

让我们来看一看关於这个程式设计问题的一种多执行绪解决方案。如程式20-3 MULTI2所示。

织梦好,好织梦

 程式20-3  MULTI2 本文来自织梦 
MULTI2.C 本文来自织梦 
/*--------------------------------------------------------------------------- 

本文来自织梦

        MULTI2.C --    Multitasking Demo 
本文来自织梦
                                                     (c) Charles Petzold, 1998 织梦好,好织梦 
----------------------------------------------------------------------------*/ 
dedecms.com
  

copyright dedecms

#include <windows.h> 本文来自织梦 
#include <math.h> copyright dedecms 
#include <process.h> 
dedecms.com
  织梦好,好织梦 
typedef struct 

dedecms.com

{ 
copyright dedecms
        HWND           hwnd ; dedecms.com 
        int            cxClient ; 

织梦好,好织梦

        int            cyClient ; 

本文来自织梦

        int            cyChar ; 

copyright dedecms

        BOOL bKill ; 本文来自织梦 
} 织梦内容管理系统 
PARAMS, *PPARAMS ; 

内容来自dedecms

LRESULT APIENTRY WndProc (HWND, UINT, WPARAM, LPARAM) ; 
内容来自dedecms
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, 织梦内容管理系统 
                    PSTR szCmdLine, int iCmdShow) dedecms.com 
{ 内容来自dedecms 
        static TCHAR   szAppName[] = TEXT ("Multi2") ; 

织梦内容管理系统

        HWND                                  hwnd ; 

copyright dedecms

        MSG                                   msg ; 

copyright dedecms

        WNDCLASS                       wndclass ; 内容来自dedecms 
      

本文来自织梦

        wndclass.style                                = CS_HREDRAW | CS_VREDRAW ; 内容来自dedecms 
        wndclass.lpfnWndProc                          = WndProc ; copyright dedecms 
        wndclass.cbClsExtra                           = 0 ; 
织梦内容管理系统
        wndclass.cbWndExtra                           = 0 ; 内容来自dedecms 
        wndclass.hInstance                            = hInstance ; 
内容来自dedecms
        wndclass.hIcon                                = LoadIcon (NULL, IDI_APPLICATION) ; 
dedecms.com
        wndclass.hCursor                              = LoadCursor (NULL, IDC_ARROW) ; dedecms.com 
        wndclass.hbrBackground                = (HBRUSH) GetStockObject (WHITE_BRUSH) ; dedecms.com 
        wndclass.lpszMenuName                 = NULL ; 
内容来自dedecms
        wndclass.lpszClassName                = szAppName ; 

内容来自dedecms

      

织梦内容管理系统

        if (!RegisterClass (&wndclass)) 织梦内容管理系统 
        { 织梦好,好织梦 
        MessageBox (NULL, TEXT ("This program requires Windows NT!"), 

内容来自dedecms

                    szAppName, MB_ICONERROR) ; 织梦好,好织梦 
                       return 0 ; dedecms.com 
        } copyright dedecms 
      

内容来自dedecms

        hwnd = CreateWindow (  szAppName, TEXT ("Multitasking Demo"), 

dedecms.com

                               WS_OVERLAPPEDWINDOW, copyright dedecms 
                               CW_USEDEFAULT, CW_USEDEFAULT, 

内容来自dedecms

                               CW_USEDEFAULT, CW_USEDEFAULT, 内容来自dedecms 
                               NULL, NULL, hInstance, NULL) ; 
织梦好,好织梦
      

dedecms.com

        ShowWindow (hwnd, iCmdShow) ; 织梦好,好织梦 
        UpdateWindow (hwnd) ; 

dedecms.com

      本文来自织梦 
        while (GetMessage (&msg, NULL, 0, 0)) 

织梦好,好织梦

        { 
dedecms.com
                       TranslateMessage (&msg) ; 
本文来自织梦
                       DispatchMessage (&msg) ; copyright dedecms 
        } 

dedecms.com

        return msg.wParam ; 
织梦内容管理系统
} 

copyright dedecms

  
本文来自织梦
int CheckBottom (HWND hwnd, int cyClient, int cyChar, int iLine) 
本文来自织梦
{ 
内容来自dedecms
        if (iLine * cyChar + cyChar > cyClient) dedecms.com 
        { 
本文来自织梦
                       InvalidateRect (hwnd, NULL, TRUE) ; 

内容来自dedecms

                       UpdateWindow (hwnd) ; 

织梦好,好织梦

                       iLine = 0 ; 
本文来自织梦
        } 
copyright dedecms
        return iLine ; 内容来自dedecms 
} dedecms.com 
  织梦内容管理系统 
// -------------------------------------------------------------------------- 
dedecms.com
// Window 1: Display increasing sequence of numbers dedecms.com 
// -------------------------------------------------------------------------- 本文来自织梦 
  织梦好,好织梦 
void Thread1 (PVOID pvoid) copyright dedecms 
{ dedecms.com 
        HDC                            hdc ; 
织梦好,好织梦
        int                            iNum = 0, iLine = 0 ; 
织梦好,好织梦
        PPARAMS                pparams ; 织梦内容管理系统 
        TCHAR                          szBuffer[16] ; 织梦内容管理系统 
      
内容来自dedecms
        pparams        = (PPARAMS) pvoid ; 本文来自织梦 
      dedecms.com 
        while (!pparams->bKill) 

dedecms.com

        { 本文来自织梦 
                       if (iNum < 0) copyright dedecms 
                                      iNum = 0 ; 内容来自dedecms 
                       iLine = CheckBottom (  pparams->hwnd,         pparams->cyClient, 
织梦好,好织梦
    pparams->cyChar,   iLine) ; 
内容来自dedecms
  织梦内容管理系统 
                       hdc = GetDC (pparams->hwnd) ; 
copyright dedecms
           

织梦内容管理系统

                       TextOut (      hdc, 0, iLine * pparams->cyChar, szBuffer,  内容来自dedecms 
                                                             wsprintf (szBuffer, TEXT ("%d"), iNum++)) ;                织梦好,好织梦 
           
织梦内容管理系统
                       ReleaseDC (pparams->hwnd, hdc) ; copyright dedecms 
                       iLine++ ; dedecms.com 
        } copyright dedecms 
        _endthread () ; 织梦内容管理系统 
} dedecms.com 
  dedecms.com 
LRESULT APIENTRY WndProc1 (    HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 本文来自织梦 
{ 
dedecms.com
        static PARAMS params ; 内容来自dedecms 
        switch (message) 内容来自dedecms 
        { 织梦内容管理系统 
        case    WM_CREATE: 
内容来自dedecms
                               params.hwnd = hwnd ; 织梦好,好织梦 
                               params.cyChar = HIWORD (GetDialogBaseUnits ()) ; 织梦好,好织梦 
                               _beginthread (Thread1, 0, ¶ms) ; 内容来自dedecms 
                               return 0 ; 织梦内容管理系统 
           内容来自dedecms 
        case    WM_SIZE: 
织梦内容管理系统
                               params.cyClient = HIWORD (lParam) ; 织梦内容管理系统 
                               return 0 ; 

本文来自织梦

           
dedecms.com
        case    WM_DESTROY: 

织梦好,好织梦

                               params.bKill = TRUE ; 

织梦好,好织梦

                               return 0 ; 
织梦好,好织梦
        } 

织梦内容管理系统

        return DefWindowProc (hwnd, message, wParam, lParam) ; 织梦内容管理系统 
} 

织梦内容管理系统

  织梦内容管理系统 
// -------------------------------------------------------------------------- 本文来自织梦 
// Window 2: Display increasing sequence of prime numbers 织梦内容管理系统 
// -------------------------------------------------------------------------- dedecms.com 
  织梦好,好织梦 
void Thread2 (PVOID pvoid) 

织梦内容管理系统

{ 

本文来自织梦

        HDC                            hdc ; dedecms.com 
        int                            iNum = 1, iLine = 0, i, iSqrt ; 
内容来自dedecms
        PPARAMS                pparams ; 织梦内容管理系统 
        TCHAR                          szBuffer[16] ; 本文来自织梦 
      内容来自dedecms 
        pparams = (PPARAMS) pvoid ; 
内容来自dedecms
        while (!pparams->bKill) copyright dedecms 
        { 

织梦内容管理系统

                       do 

copyright dedecms

                       { 
dedecms.com
                               if (++iNum < 0) dedecms.com 
                                                     iNum = 0 ; 
内容来自dedecms
                               iSqrt = (int) sqrt (iNum) ; 织梦好,好织梦 
                               for (i = 2 ; i <= iSqrt ; i++) 织梦内容管理系统 
                                                     if (iNum % i == 0) 本文来自织梦 
                                                                            break ; 内容来自dedecms 
                       } 
织梦好,好织梦
                       while (i <= iSqrt) ; 织梦好,好织梦 
                       iLine = CheckBottom    (       pparams->hwnd,        pparams->cyClient, 

本文来自织梦

    pparams->cyChar,   iLine) ; 
织梦内容管理系统
           
copyright dedecms
                       hdc = GetDC (pparams->hwnd) ; 织梦好,好织梦 
           

织梦好,好织梦

                       TextOut (      hdc, 0, iLine * pparams->cyChar, szBuffer,  织梦好,好织梦 
                                                             wsprintf (szBuffer, TEXT ("%d"), iNum)) ; 

本文来自织梦

           dedecms.com 
                       ReleaseDC (pparams->hwnd, hdc) ; 

copyright dedecms

                       iLine++ ; copyright dedecms 
        } 内容来自dedecms 
        _endthread () ; 

织梦好,好织梦

} 
dedecms.com
  
织梦内容管理系统
LRESULT APIENTRY WndProc2 (    HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 本文来自织梦 
{ dedecms.com 
        static PARAMS params ; 织梦内容管理系统 
        switch (message) 
dedecms.com
        { 

内容来自dedecms

        case    WM_CREATE: 
织梦内容管理系统
                               params.hwnd = hwnd ; 

织梦好,好织梦

                               params.cyChar = HIWORD (GetDialogBaseUnits ()) ; 

内容来自dedecms

                               _beginthread (Thread2, 0, ¶ms) ; 

织梦内容管理系统

                               return 0 ; 

本文来自织梦

           

织梦好,好织梦

        case    WM_SIZE: 

织梦好,好织梦

                               params.cyClient = HIWORD (lParam) ; 内容来自dedecms 
                               return 0 ; 织梦好,好织梦 
           
内容来自dedecms
        case    WM_DESTROY: copyright dedecms 
                               params.bKill = TRUE ; 
织梦内容管理系统
                               return 0 ; 内容来自dedecms 
        } 
织梦好,好织梦
        return DefWindowProc (hwnd, message, wParam, lParam) ; 

copyright dedecms

} 

copyright dedecms

  dedecms.com 
// Window 3: Display increasing sequence of Fibonacci numbers 
内容来自dedecms
// ---------------------------------------------------------- 

织梦内容管理系统

  

内容来自dedecms

void Thread3 (PVOID pvoid) dedecms.com 
{ copyright dedecms 
        HDC                            hdc ; 本文来自织梦 
        int                    iNum = 0, iNext = 1, iLine = 0, iTemp ; 
copyright dedecms
        PPARAMS                pparams ; copyright dedecms 
        TCHAR                          szBuffer[16] ; 
dedecms.com
      
本文来自织梦
        pparams = (PPARAMS) pvoid ; 
本文来自织梦
        while (!pparams->bKill) 内容来自dedecms 
     { 本文来自织梦 
                               if (iNum < 0) 织梦好,好织梦 
                               { 
本文来自织梦
                                      iNum  = 0 ; 
织梦内容管理系统
                                      iNext = 1 ; 本文来自织梦 
                       } 本文来自织梦 
                       iLine = CheckBottom (  pparams->hwnd,         pparams->cyClient, 本文来自织梦 
    pparams->cyChar, iLine) ; 

织梦好,好织梦

  

织梦内容管理系统

                       hdc = GetDC (pparams->hwnd) ; copyright dedecms 
           
copyright dedecms
                       TextOut (hdc, 0, iLine * pparams->cyChar, szBuffer,  dedecms.com 
                                                             wsprintf (szBuffer, TEXT ("%d"), iNum)) ; copyright dedecms 
           
dedecms.com
                       ReleaseDC (pparams->hwnd, hdc) ; copyright dedecms 
                       iTemp  = iNum ; copyright dedecms 
                       iNum   = iNext ; 本文来自织梦 
                       iNext += iTemp ; 本文来自织梦 
                       iLine++ ; dedecms.com 
        } 

织梦内容管理系统

        _endthread () ; 织梦内容管理系统 
} 本文来自织梦 
  本文来自织梦 
LRESULT APIENTRY WndProc3 (    HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 

内容来自dedecms

{ 

本文来自织梦

        static PARAMS params ; 本文来自织梦 
        switch (message) 
内容来自dedecms
        { 

织梦好,好织梦

        case    WM_CREATE: 本文来自织梦 
                               params.hwnd = hwnd ; 

内容来自dedecms

                               params.cyChar = HIWORD (GetDialogBaseUnits ()) ; 
内容来自dedecms
                               _beginthread (Thread3, 0, ¶ms) ; copyright dedecms 
                               return 0 ; 
dedecms.com
  

dedecms.com

        case    WM_SIZE: 织梦内容管理系统 
                               params.cyClient = HIWORD (lParam) ; 
copyright dedecms
                               return 0 ; 
copyright dedecms
           
本文来自织梦
        case    WM_DESTROY: 内容来自dedecms 
                               params.bKill = TRUE ; 内容来自dedecms 
                               return 0 ; dedecms.com 
        } copyright dedecms 
        return DefWindowProc (hwnd, message, wParam, lParam) ; 本文来自织梦 
} dedecms.com 
  内容来自dedecms 
// ------------------------------------------------------------------------- 
copyright dedecms
// Window 4: Display circles of random radii 织梦内容管理系统 
// ------------------------------------------------------------------------- dedecms.com 
  本文来自织梦 
void Thread4 (PVOID pvoid) 
内容来自dedecms
{ 
copyright dedecms
        HDC                            hdc ; 
内容来自dedecms
        int                            iDiameter ; 

织梦好,好织梦

        PPARAMS                pparams ; 内容来自dedecms 
      

dedecms.com

        pparams = (PPARAMS) pvoid ; 
dedecms.com
        while (!pparams->bKill) copyright dedecms 
        { 

织梦好,好织梦

                       InvalidateRect (pparams->hwnd, NULL, TRUE) ; 内容来自dedecms 
                       UpdateWindow (pparams->hwnd) ; 

copyright dedecms

           

织梦好,好织梦

                       iDiameter =    rand() % (max (1, 本文来自织梦 
                min (pparams->cxClient, pparams->cyClient))) ; 本文来自织梦 
           dedecms.com 
                       hdc = GetDC (pparams->hwnd) ; 
dedecms.com
           织梦内容管理系统 
                       Ellipse (hdc, (pparams->cxClient - iDiameter) / 2, 织梦好,好织梦 
                                      (pparams->cyClient - iDiameter) / 2, 

dedecms.com

                                      (pparams->cxClient + iDiameter) / 2, 
dedecms.com
                                   (pparams->cyClient + iDiameter) / 2) ; dedecms.com 
           
dedecms.com
                       ReleaseDC (pparams->hwnd, hdc) ; 

copyright dedecms

        } 
本文来自织梦
        _endthread () ; 
dedecms.com
} 本文来自织梦 
  
织梦内容管理系统
LRESULT APIENTRY WndProc4      (HWND hwnd, UINT message,WPARAM wParam,LPARAM lParam) 织梦内容管理系统 
{ 
内容来自dedecms
        static PARAMS params ; 

本文来自织梦

        switch (message) dedecms.com 
        { 本文来自织梦 
        case    WM_CREATE: dedecms.com 
                               params.hwnd = hwnd ; 
织梦内容管理系统
                               params.cyChar = HIWORD (GetDialogBaseUnits ()) ; 内容来自dedecms 
                               _beginthread (Thread4, 0, ¶ms) ; 织梦内容管理系统 
                               return 0 ; dedecms.com 
           织梦好,好织梦 
        case    WM_SIZE: 内容来自dedecms 
                               params.cxClient = LOWORD (lParam) ; 
织梦好,好织梦
                               params.cyClient = HIWORD (lParam) ; 
dedecms.com
                               return 0 ; 

本文来自织梦

           

织梦内容管理系统

        case    WM_DESTROY: 

本文来自织梦

                               params.bKill = TRUE ; 
本文来自织梦
                               return 0 ; 
内容来自dedecms
        } 织梦好,好织梦 
        return DefWindowProc (hwnd, message, wParam, lParam) ; dedecms.com 
} 
织梦内容管理系统
  织梦内容管理系统 
// -------------------------------------------------------------------------- 
织梦内容管理系统
// Main window to create child windows 
内容来自dedecms
// -------------------------------------------------------------------------- 内容来自dedecms 
  

copyright dedecms

LRESULT APIENTRY WndProc (     HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 
dedecms.com
{ 

内容来自dedecms

        static HWND            hwndChild[4] ; 本文来自织梦 
        static TCHAR * szChildClass[] = { TEXT ("Child1"), TEXT ("Child2"), 本文来自织梦 
        TEXT ("Child3"), TEXT ("Child4") } ; 内容来自dedecms 
        static WNDPROC ChildProc[] = { WndProc1, WndProc2, WndProc3, WndProc4 } ; 
dedecms.com
        HINSTANCE              hInstance ; 
copyright dedecms
        int                                   i, cxClient, cyClient ; 
内容来自dedecms
        WNDCLASS                       wndclass ; 
织梦内容管理系统
      dedecms.com 
        switch (message) copyright dedecms 
        { 内容来自dedecms 
        case    WM_CREATE: 
本文来自织梦
                               hInstance = (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE) ; 本文来自织梦 
                               wndclass.style                                               = CS_HREDRAW | CS_VREDRAW ; 织梦好,好织梦 
                               wndclass.cbClsExtra                           = 0 ; 

copyright dedecms

                               wndclass.cbWndExtra                           = 0 ; 
本文来自织梦
                               wndclass.hInstance                            = hInstance ; 本文来自织梦 
                               wndclass.hIcon                                = NULL ; 内容来自dedecms 
                               wndclass.hCursor                             = LoadCursor (NULL, IDC_ARROW) ; 内容来自dedecms 
                               wndclass.hbrBackground                = (HBRUSH) GetStockObject (WHITE_BRUSH) ; dedecms.com 
                               wndclass.lpszMenuName  = NULL ; 

dedecms.com

           

内容来自dedecms

                               for (i = 0 ; i < 4 ; i++) 
织梦内容管理系统
                               { 

织梦内容管理系统

                                      wndclass.lpfnWndProc           = ChildProc[i] ; 织梦好,好织梦 
                                      wndclass.lpszClassName         = szChildClass[i] ; 

copyright dedecms

                dedecms.com 
                                      RegisterClass (&wndclass) ; copyright dedecms 
                
copyright dedecms
                                      hwndChild[i] = CreateWindow (szChildClass[i], NULL, 

织梦好,好织梦

                            WS_CHILDWINDOW | WS_BORDER | WS_VISIBLE, dedecms.com 
                            0, 0, 0, 0,  
dedecms.com
                            hwnd, (HMENU) i, hInstance, NULL) ; copyright dedecms 
                               } 内容来自dedecms 
           

copyright dedecms

                               return 0 ; 

本文来自织梦

           

织梦内容管理系统

        case    WM_SIZE: 织梦好,好织梦 
                               cxClient = LOWORD (lParam) ; 
内容来自dedecms
                               cyClient = HIWORD (lParam) ; 本文来自织梦 
           

copyright dedecms

                               for (i = 0 ; i < 4 ; i++) 

本文来自织梦

                                              MoveWindow (hwndChild[i], (i % 2) * cxClient / 2, 

dedecms.com

                                                                      (i > 1) * cyClient / 2, 本文来自织梦 
                                                  cxClient / 2, cyClient / 2, TRUE) ; 织梦好,好织梦 
                               return 0 ; 
copyright dedecms
           织梦好,好织梦 
        case    WM_CHAR: 

内容来自dedecms

                               if (wParam == '\x1B') 织梦好,好织梦 
                                              DestroyWindow (hwnd) ; 

织梦内容管理系统

           织梦内容管理系统 
                               return 0 ; 本文来自织梦 
           
织梦内容管理系统
        case    WM_DESTROY: 

copyright dedecms

                               PostQuitMessage (0) ; dedecms.com 
                               return 0 ; 
copyright dedecms
        } 本文来自织梦 
        return DefWindowProc (hwnd, message, wParam, lParam) ; dedecms.com 
} 

织梦好,好织梦

MULTI2.CWinMainWndProc函式非常类似於MULTI1.C中的同名函式。WndProc为四个视窗注册了四种视窗类别,建立了这些视窗,并在WM_SIZE讯息处理期间缩放这些视窗。WndProc的唯一不同是它不再设定Windows计时器,也不再处理WM_TIMER讯息。 织梦内容管理系统

MULTI2中较大的改变是每个子视窗讯息处理程式透过在WM_CREATE讯息处理期间呼叫_beginthread函式来建立另一个执行绪。总括来说,MULTI2程式有五个同时执行的执行绪,主执行绪包含主视窗讯息处理程式和四个子视窗讯息处理程式,其余的四个执行绪使用名为Thread1Thread2等的函式,这四个执行绪负责绘制四个视窗。

copyright dedecms

我在RNDRCTMT程式中给出的多执行绪程式码没有使用_beginthread的第三个参数,这个参数允许一个建立另一个执行绪的执行绪在32位元变数中将资讯传递给其他执行绪。通常,这个变数是一个指标,而且是指向一个结构的指标,这允许原来的执行绪和新执行绪共用资讯,而不必借助於整体变数。您可以看到,在MULTI2中没有整体变数。 织梦内容管理系统

MULTI2程式,我在程式开头定义了一个名为PARAMS的结构和一个名为PPARAMS的指向结构的指标,这个结构有五个栏位-视窗代号、视窗的宽度和高度、字元的高度和名为bKill的布林变数。最後的结构栏位允许建立执行绪告知被建立执行绪何时终止。 织梦内容管理系统

让我们来看一看WndProc1,这是显示增加数序列的子视窗讯息处理程式。视窗讯息处理程式变得非常简单,唯一的区域变数是一个PARAMS结构。在WM_CREATE讯息处理期间,它设定这个结构的hwndcyChar栏位,呼叫_beginthread来建立一个使用Thread1函式的新执行绪,并传递给新执行绪一个指向该结构的指标。在WM_SIZE讯息处理期间,WndProc1设定结构的cyClient栏位,而在WM_DESTROY讯息处理期间,它将bKill栏位设定为TRUEThread1函式通过对_endthread的呼叫而告结束。这并不是绝对必要的,因为执行绪将在退出执行绪函式之後被清除。不过,要退出一个深陷入复杂的处理程序的执行绪时,_endthread是很有用的。 dedecms.com

Thread1函式完成在视窗上的实际绘图,并且和程式的其他四个执行绪同时执行。函式接收指向PARAMS结构的一个指标,并进入一个while回圈,不断检查bKillTRUE还是FALSE。如果是FALSE,那么函式必须进行MULTI1.C中的WM_TIMER讯息处理期间所作的同样处理-格式化数字、取得装置内容代号并使用TextOut显示数字。

织梦内容管理系统

当您在Windows 98中执行MULTI2时,将会看到,视窗更新要比在MULTI1中快得多,这表示程式在更加有效地利用处理器的资源。在MULTI1MULTI2之间还有另一种区别:通常,当您移动或者缩放一个视窗时,内定视窗讯息处理程式进入一种模态回圈,而视窗的所有输出都将停止。在MULTI2中,输出将继续。 织梦好,好织梦

有问题吗?
  dedecms.com

似乎MULTI2程式并没有达到它应该有的稳固性。我为什么会这样认为呢?让我们来看一看MULTI2.C中的一些多执行绪「缺陷」,以WndProc1Thread1为例。

内容来自dedecms

WndProc1MULTI2的主执行绪中执行,而Thread1与它同时执行,Windows 98在这两个执行绪之间进行切换是不可预测的。假定Thread1正在执行,并且刚好执行了检查PARAMS结构的bKill栏位是否为TRUE的程式码。发现不为TRUE,但是这之後Windows 98将控制权切换到主执行绪,这时使用者终止了程式,WndProc1收到一个WM_DESTROY讯息并将bKill参数设为TRUE。哦,这参数设定得太晚了!作业系统突然切换到Thread1中,而该函式会试图取得一个不存在的视窗的装置内容代号。

本文来自织梦

事实证明,这不是一个问题。Windows 98够稳固,以致另一条执行绪呼叫的图形处理函式只是失败而已,而不会引起任何问题。 本文来自织梦

正确的多执行绪程式写作技术涉及执行绪同步的使用(尤其是临界区域的使用),我将马上加以详细地讨论。大体上,临界区域通过对EnterCriticalSectionLeaveCriticalSection的呼叫而加以界定。如果一个执行绪进入一个临界区域,那么另一个执行绪将无法再进入这个临界区域。後一个执行绪被阻档在对EnterCriticalSection的呼叫上,直到第一个执行绪呼叫LeaveCriticalSection时为止。

织梦内容管理系统

MULTI2中的另一个可能存在的问题是,当另外一个执行绪显示其输出时,主执行绪可能会收到一个WM_ERASEBKGNDWM_PAINT讯息。这里,使用临界区域有助於避免当两个程序试图在同一个视窗上绘图时可能导致的任何问题。但是,经验显示,Windows 98很恰当地序列化了对图形绘制函式的存取。亦即,当另一个执行绪正在绘图的时候,一个执行绪不能在同一个视窗上绘图。

copyright dedecms

Windows 98文件提醒说,有一种未进行图形函式序列化的情形,这就是GDI物件(如画笔、画刷、字体、点阵图、区域和调色盘等)的使用。有可能发生一个执行绪清除了一个物件,而另一个执行绪仍然在使用它的情况。解决这个问题的方法要求使用临界区域,或者最好不要在执行绪之间共用GDI物件。 内容来自dedecms

Sleep的好处
  dedecms.com

我曾经提到,我认为对一个多执行绪程式来说,最好的架构是主执行绪建立程式中的所有视窗,以及所有的视窗讯息处理程式,并处理所有的视窗讯息。其他执行绪完成背景工作或者冗长作业。 copyright dedecms

不过,假设您想在另一个执行绪中做动画。通常,Windows中的动画是使用WM_TIMER讯息来实作的。如果这个执行绪没有建立视窗,那么它也不会收到这些讯息。如果没有计时器,动画又可能会执行得太快。 dedecms.com

解决方案是Sleep函式。实际上,执行绪呼叫Sleep函式来自动暂停执行,该函式唯一一个参数是以毫秒计的时间。Sleep函式呼叫在指定的时间过去以前不会传回控制权。在这段时间内,执行绪被暂停,并且不会被配置给时间片段(尽管该执行绪显然仍然要求在tick时给予一小段的处理时间,因为系统必须确定执行绪是否应该重新开始执行)。给Sleep一个值为0的参数将导致执行绪交回它尚未使用完的时间片段。 copyright dedecms

当一个执行绪呼叫Sleep时,只是该执行绪被暂停指定的时间。系统仍然执行其他的执行绪,这些执行绪和暂停的执行绪可以是在同一个程序中,也可以是在另一个程序中。我在第十四章中的SCRAMBLE程式中使用了Sleep函式,以放慢画面清除的操作。 dedecms.com

通常,您不应该在您的主执行绪中使用Sleep函式,因为这会减慢对讯息的处理速度,但是因为SCRAMBLE没有建立任何视窗,因此在那里使用Sleep应该没有问题。 本文来自织梦

执行绪同步
 

本文来自织梦

大约每年一次,在我公寓窗外的交通繁忙地段的红绿灯会停止工作。结果是造成交通的混乱,虽然轿车一般能避免撞上别的轿车,但是这些车经常挤在一起。 copyright dedecms

我用术语称两条路相交的十字路口为「临界区域」。一辆向南的车和一辆向西的车不可能同时通过一个十字路口而不撞著对方。依赖於交通流量,可以采用不同的方法来解决这个问题。对於视野清楚车辆稀少的路口,可以相信司机有处理的能力。车辆增多可能会要求一个停车号志,而更加繁忙的交通则将要求有红绿灯,红绿灯有助於协调路口的交通(当然,这些灯号必须正常工作)。 织梦内容管理系统

临界区域
  本文来自织梦

在单工作业系统中,传统的电脑程式不需要红绿灯来帮助协调它们之间的行为。它们在执行时似乎独占了整条路,而且也确实是这样,没有什么会干扰它们的工作。

dedecms.com

即使在多工作业系统中,大多数的程式也似乎各自独立地在执行,但是可能会发生一些问题。例如,两个程式可能会需要同时从同一个档案中读或者对同一档案进行写。在这种情况下,作业系统提供了一种共用档案和记录上锁的技术来帮助解决这个问题。 copyright dedecms

然而,在支援多执行绪的作业系统中,情况会变得混乱而且存在潜在的危险。两个或多个执行绪共用某些资料的情况并不罕见。例如,一个执行绪可以更新一个或者多个变数,而另一个执行绪可以使用这些变数。有时这会引发一个问题,有时又不会(记住作业系统将控制权从一个执行绪切换到另一个执行绪的操作,只能在机器码指令之间发生。如果只是一个整数被执行绪共用,那么对这个变数的改变通常发生在单个指令中,因此潜在的问题被最小化了)。

dedecms.com

然而,假设执行绪共用几个变数或者资料结构。通常,这么多个变数或者结构的栏位在它们之间必须是一致的。作业系统可以在更新这些变数的程序中间中断一个执行绪,那么使用这些变数的执行绪得到的将是不一致的资料。 dedecms.com

结果是冲突发生了,并且通常不难想像这样的错误将对程式造成怎样的破坏。我们所需要的是类似於红绿灯的程式写作技术,以帮助我们对执行绪交通进行协调和同步,这就是临界区域。大体上,一个临界区域就是一块不可中断的程式码。 本文来自织梦

有四个函式用於临界区域。要使用这些函式,您必须定义一个临界区域物件,这是一个型态为CRITICAL_SECTION的整体变数。例如:

本文来自织梦

CRITICAL_SECTION cs ; 
织梦好,好织梦

这个CRITICAL_SECTION资料型态是一个结构,但是其中的栏位只能由Windows内部使用。这个临界区域物件必须先被程式中的某个执行绪初始化,通过呼叫: 内容来自dedecms

InitializeCriticalSection (&cs) ; 内容来自dedecms 

这样就建立了一个名为cs的临界区域物件。该函式的线上辅助说明包含下面的警告:「临界区域物件不能被移动或者复制,程序也不能修改该物件,但必须在逻辑上把它视为不透明的。」这句话,可以被解释为:「不要干扰它,甚至不要看它。」 内容来自dedecms

当临界区域物件被初始化之後,执行绪可以通过下面的呼叫进入临界区域:

内容来自dedecms

EnterCriticalSection (&cs) ; 

dedecms.com

在这时,执行绪被认为「拥有」临界区域物件。两个执行绪不可以同时拥有同一个临界区域物件,因此,如果一个执行绪进入了临界区域,那么下一个使用同一临界区域物件呼叫EnterCriticalSection的执行绪将在函式呼叫中被暂停。只有当第一个执行绪通过下面的呼叫离开临界区域时,函式才会传回控制权:

本文来自织梦

LeaveCriticalSection (&cs) ; 织梦好,好织梦 

这时,在EnterCriticalSection呼叫中被停住的那个执行绪将拥有临界区域,其函式呼叫也将传回,允许执行绪继续执行。

织梦内容管理系统

当临界区域不再被程式所需要时,可以通过呼叫

copyright dedecms

DeleteCriticalSection (&cs) ; dedecms.com 

将其删除,该函式释放所有被配置来维护此临界区域物件的系统资源。 本文来自织梦

这种临界区域技术涉及「互斥」(此术语在我们继续讨论执行绪同步时将再次出现)。在任何时刻,只有一个执行绪能拥有一个临界区域。因此,一个执行绪可以进入一个临界区域,设定一个结构的栏位,然後退出临界区域。另一个使用该结构的执行绪在存取结构中的栏位之前也要先进入该临界区域,然後再退出临界区域。 dedecms.com

注意,您可以定义多个临界区域物件,比如cs1cs2。例如,如果一个程式有四个执行绪,而前两个执行绪共用一些资料,那么它们可以使用一个临界区域物件,而另外两个执行绪共用一些其他的资料,那么它们可以使用另一个临界区域物件。 织梦好,好织梦

您在主执行绪中使用临界区域时应该小心。如果从属执行绪在它自己的临界区域中花费了一段很长的时间,那么它可能会将主执行绪的执行阻碍很长一段时间。从属执行绪可能只是使用临界区域复制该结构的栏位到自己的区域变数中。 内容来自dedecms

临界区域的一个限制是它们只能用於在同一程序内的执行绪之间的协调。但是在某些情况下,您需要协调两个不同程序对同一资源的共用(如共用记忆体等)。在此其况下不能使用临界区域,但是可以使用一种被称为「互斥物件(mutex object)」的技术。「mutex」是个合成字,代表「mutual exclusion(互斥)」,它在这里精确地表达了我们的目的。我们想防止一个程式的执行绪在更新资料或者使用共用记忆体与其他资源时被中断。 copyright dedecms

事件信号
 

dedecms.com

多执行绪通常是用於那些必须执行长时间处理的程式。我们可以将一个「大作业」定义为一个可能会违反1/10秒规则的程式。显然大作业包括文书处理程式中的拼写检查、资料库程式中的档案排序或者索引、试算表的重新计算、列印,甚至包括复杂的绘图。当然,迄今为止我们知道,遵循1/10秒规则的最好方法是将大作业放到另一个执行绪去执行。这些额外的执行绪不会建立视窗,因此它们不受1/10秒规则的限制。 织梦好,好织梦

通常希望这些额外的执行绪在完成其任务时能够通知主执行绪,或者主执行绪能够停止其他执行绪正在进行的作业。这就是我们下面将要讨论的。 内容来自dedecms

BIGJOB1程式
  内容来自dedecms

作为一个想像的大作业,我将使用一系列浮点运算,有时这种运算被称为「暴力的」性能测试指标。这种计算以一种间接的方式递增一个整数的值:它求一个数的平方,再对结果取平方根(得到原来的整数),然後使用logexp函式(同样得到原来的整数),接著使用atantan函式(还是得到原来的整数),最後对结果加1dedecms.com

BIGJOB1程式如程式20-4所示。 内容来自dedecms

 程式20-4  BIGJOB1 dedecms.com 
BIGJOB1.C copyright dedecms 
/*--------------------------------------------------------------------------- 织梦内容管理系统 
        BIGJOB1.C -- Multithreading Demo 织梦内容管理系统 
                                                             (c) Charles Petzold, 1998 

dedecms.com

----------------------------------------------------------------------------*/ 

内容来自dedecms

  
织梦内容管理系统
#include <windows.h> 本文来自织梦 
#include <math.h> 内容来自dedecms 
#include <process.h> 织梦内容管理系统 
  

dedecms.com

#define REP                                                          1000000 dedecms.com 
  
织梦好,好织梦
#define STATUS_READY                          0 dedecms.com 
#define STATUS_WORKING                    1 内容来自dedecms 
#define STATUS_DONE                           2 dedecms.com 
  

织梦好,好织梦

#define WM_CALC_DONE                  (WM_USER + 0) 
织梦内容管理系统
#define WM_CALC_ABORTED               (WM_USER + 1) 织梦好,好织梦 
  dedecms.com 
typedef struct copyright dedecms 
{ 本文来自织梦 
        HWND hwnd ; 内容来自dedecms 
        BOOL bContinue ; 
本文来自织梦
} 内容来自dedecms 
PARAMS, *PPARAMS ; 织梦好,好织梦 
LRESULT APIENTRY WndProc (HWND, UINT, WPARAM, LPARAM) ; copyright dedecms 
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, dedecms.com 
                    szCmdLine, int iCmdShow) copyright dedecms 
{ 本文来自织梦 
        static TCHAR           szAppName[] = TEXT ("BigJob1") ; 

织梦内容管理系统

        HWND                                          hwnd ; 内容来自dedecms 
        MSG                                           msg ; 

织梦内容管理系统

        WNDCLASS                              wndclass ; 

copyright dedecms

        wndclass.style                        = CS_HREDRAW | CS_VREDRAW ; 内容来自dedecms 
        wndclass.lpfnWndProc                  = WndProc ; 织梦内容管理系统 
        wndclass.cbClsExtra                   = 0 ; 织梦好,好织梦 
        wndclass.cbWndExtra                   = 0 ; 内容来自dedecms 
        wndclass.hInstance                    = hInstance ; dedecms.com 
        wndclass.hIcon                        = LoadIcon (NULL, IDI_APPLICATION) ; 

内容来自dedecms

        wndclass.hCursor               = LoadCursor (NULL, IDC_ARROW) ; 

织梦内容管理系统

        wndclass.hbrBackground         = (HBRUSH) GetStockObject (WHITE_BRUSH) ; dedecms.com 
        wndclass.lpszMenuName          = NULL ; 
本文来自织梦
        wndclass.lpszClassName         = szAppName ; 内容来自dedecms 
      

织梦内容管理系统

        if (!RegisterClass (&wndclass)) 
织梦好,好织梦
        { 

dedecms.com

        MessageBox (   NULL, TEXT ("This program requires Windows NT!"), 织梦好,好织梦 
                      szAppName, MB_ICONERROR) ; 
织梦好,好织梦
                       return 0 ; 本文来自织梦 
     } 

dedecms.com

      织梦好,好织梦 
        hwnd = CreateWindow (  szAppName, TEXT ("Multithreading Demo"), 

copyright dedecms

                                      WS_OVERLAPPEDWINDOW, 内容来自dedecms 
                                      CW_USEDEFAULT, CW_USEDEFAULT, copyright dedecms 
                                      CW_USEDEFAULT, CW_USEDEFAULT, 
内容来自dedecms
                                      NULL, NULL, hInstance, NULL) ; 织梦好,好织梦 
      织梦好,好织梦 
        ShowWindow (hwnd, iCmdShow) ; 本文来自织梦 
        UpdateWindow (hwnd) ; 

织梦好,好织梦

      dedecms.com 
        while (GetMessage (&msg, NULL, 0, 0)) 
本文来自织梦
        { 

织梦内容管理系统

                       TranslateMessage (&msg) ; 

copyright dedecms

                       DispatchMessage (&msg) ; dedecms.com 
        } 
织梦好,好织梦
        return msg.wParam ; 织梦好,好织梦 
} 
织梦好,好织梦
  织梦好,好织梦 
void Thread (PVOID pvoid) 本文来自织梦 
{ 

本文来自织梦

        double                 A = 1.0 ; 
内容来自dedecms
        INT                            i ; 
本文来自织梦
        LONG                           lTime ; 
本文来自织梦
        volatile               PPARAMS pparams ; 织梦内容管理系统 
      dedecms.com 
        pparams = (PPARAMS) pvoid ; 织梦内容管理系统 
        lTime = GetCurrentTime () ; 织梦好,好织梦 
        for (i = 0 ; i < REP && pparams->bContinue ; i++) 
内容来自dedecms
                                      A = tan (atan (exp (log (sqrt (A * A))))) + 1.0 ; 

dedecms.com

        if (i == REP) 织梦内容管理系统 
        { 
dedecms.com
                       lTime = GetCurrentTime () - lTime ; 

织梦内容管理系统

                       SendMessage (pparams->hwnd, WM_CALC_DONE, 0, lTime) ; 

copyright dedecms

        } 织梦内容管理系统 
        else copyright dedecms 
                       SendMessage (pparams->hwnd, WM_CALC_ABORTED, 0, 0) ; 内容来自dedecms 
        _endthread () ; 

dedecms.com

} 织梦好,好织梦 
  
copyright dedecms
LRESULT CALLBACK WndProc (     HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 
dedecms.com
{ 织梦好,好织梦 
        static INT            iStatus ; 内容来自dedecms 
        static LONG           lTime ; dedecms.com 
        static PARAMS  params ; dedecms.com 
        static         TCHAR *szMessage[] = { TEXT ("Ready (left mouse button begins)"), 
内容来自dedecms
                    TEXT ("Working (right mouse button ends)"), 织梦内容管理系统 
                    TEXT ("%d repetitions in %ld msec") } ; 
织梦好,好织梦
        HDC                                           hdc ; 

本文来自织梦

        PAINTSTRUCT                            ps ; 

copyright dedecms

        RECT                                          rect ; 织梦好,好织梦 
        TCHAR                                         szBuffer[64] ; copyright dedecms 
      织梦好,好织梦 
        switch (message) 
内容来自dedecms
        { 织梦内容管理系统 
        case    WM_LBUTTONDOWN: 织梦好,好织梦 
                               if (iStatus == STATUS_WORKING) 本文来自织梦 
                               { 织梦好,好织梦 
                                      MessageBeep (0) ; 
dedecms.com
                                      return 0 ; 

内容来自dedecms

                               } 
内容来自dedecms
           织梦内容管理系统 
                               iStatus = STATUS_WORKING ; 织梦内容管理系统 
           
内容来自dedecms
                               params.hwnd = hwnd ; 
dedecms.com
                               params.bContinue = TRUE ; 

织梦好,好织梦

           内容来自dedecms 
                               _beginthread (Thread, 0, ¶ms) ; 
内容来自dedecms
           织梦好,好织梦 
                               InvalidateRect (hwnd, NULL, TRUE) ; 织梦好,好织梦 
                               return 0 ; 

本文来自织梦

          
织梦内容管理系统
        case    WM_RBUTTONDOWN: dedecms.com 
                               params.bContinue = FALSE ; 
copyright dedecms
                               return 0 ; 织梦好,好织梦 
           
织梦内容管理系统
        case    WM_CALC_DONE: dedecms.com 
                               lTime = lParam ; 

织梦好,好织梦

                               iStatus = STATUS_DONE ; 内容来自dedecms 
                               InvalidateRect (hwnd, NULL, TRUE) ; 织梦好,好织梦 
                               return 0 ; 内容来自dedecms 
           
内容来自dedecms
        case    WM_CALC_ABORTED: 
dedecms.com
                               iStatus = STATUS_READY ; 

内容来自dedecms

                               InvalidateRect (hwnd, NULL, TRUE) ; 织梦内容管理系统 
                               return 0 ; 内容来自dedecms 
           织梦好,好织梦 
        case    WM_PAINT: 
copyright dedecms
                               hdc = BeginPaint (hwnd, &ps) ; 织梦内容管理系统 
           dedecms.com 
                               GetClientRect (hwnd, &rect) ; 
内容来自dedecms
           内容来自dedecms 
                               wsprintf (szBuffer, szMessage[iStatus], REP, lTime) ; 

本文来自织梦

                               DrawText (hdc, szBuffer, -1, &rect, 织梦好,好织梦 
                                 DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; 本文来自织梦 
           
copyright dedecms
                               EndPaint (hwnd, &ps) ; 
织梦好,好织梦
                               return 0 ; 织梦好,好织梦 
           内容来自dedecms 
        case    WM_DESTROY: 

本文来自织梦

                               PostQuitMessage (0) ; 

织梦内容管理系统

                               return 0 ; 内容来自dedecms 
        } 
本文来自织梦
        return DefWindowProc (hwnd, message, wParam, lParam) ; 本文来自织梦 
} dedecms.com 

这是一个相当简单的程式,但是我认为您将看到它如何展示在多执行绪程式中完成大作业的通用方法。为了使用BIGJOB1程式,在视窗的显示区域中按下滑鼠左键,从而开始暴力的性能测试计算的1,000,000次重复,这在一台300MHzPentium II机器上将花费2秒。当完成计算时,花费的时间将显示在视窗上。当正在进行计算时,您可以通过在显示区域中按下滑鼠右键来终止它。

copyright dedecms

让我们来看一看这是如何实作的:

dedecms.com

视窗讯息处理程式拥有了一个被叫做iStatus的静态变数(该变数可以被设定为在程式开始处定义的三个常数之一,常数以STATUS为字首),该变数表示程式是否准备好进行一次计算,是否正在进行一次计算,或者是否完成了计算。程式在WM_PAINT讯息处理期间使用iStatus变数在显示区域的中央显示一个适当的字串。 copyright dedecms

视窗讯息处理程式还拥有一个静态结构(型态为PARAMS,也定义在程式的顶部),该结构是在视窗讯息处理程式和其他执行绪之间的共用资料。结构只有两个栏位-hwnd(程式视窗的代号)和bContinue,这是一个布林变数,用於指示执行绪是否继续计算或者停止。 copyright dedecms

当您在显示区域中按下滑鼠左键时,视窗讯息处理程式将iStatus变数设为STATUS_WORKING,并设定PARAMS结构中的两个栏位。结构的hwnd栏位被设定为视窗代号,当然,bContinue被设定为TRUE

织梦好,好织梦

然後视窗程序呼叫_beginthread函式。执行绪函式Thread以呼叫GetCurrentTime开始,GetCurrentTime取得以毫秒计的Windows启动以来已经执行了的时间。然後它进入一个for回圈,重复1,000,000次的暴力测试计算。还要注意,如果bContinue被设为了FALSE,那么执行绪将退出回圈。 copyright dedecms

for回圈之後,执行绪函式检查它是否确实完成了1,000,000次计算。如果是,那么它再次呼叫GetCurrentTime获得所经过的时间,然後使用SendMessage向视窗讯息处理程式发送一个由程式定义的WM_USER_DONE讯息,并以经过的时间作为lParam参数。如果计算是在未完成之前被终止的(即,如果在回圈期间PARAMS结构的bContinue栏位变为FALSE),那么执行绪将发送给视窗讯息处理程式一个WM_USER_ABORTED讯息。然後,执行绪通过呼叫_endthread正常地结束。 本文来自织梦

在视窗讯息处理程式中,当您在显示区域中按下滑鼠右键时,PARAMS结构的bContinue栏位被设为FALSE。这是如何在完成计算之前结束计算的方法。 本文来自织梦

注意Thread中的pparams变数定义为volatile,这种型态限定字向编译器指出变数可能会在实际的程式叙述外被修改(例如被另一个执行绪)。否则,最佳化的编译器会假设pparams->bContinue不能被for回圈内的程式码修改,没有必要在每层回圈中检查变数。volatile关键字防止这样的最佳化进行。

copyright dedecms

视窗讯息处理程式处理WM_USER_DONE讯息时,首先储存经过的时间。对WM_USER_DONEWM_USER_ABORTED讯息的处理都是透过对InvalidateRect的呼叫产生WM_PAINT讯息并在显示区域显示一个新的字串。

本文来自织梦

提供一个方法(如结构中的bContinue栏位)允许执行绪正常终止,通常是一个好主意。KillThread函式只有在正常终止执行绪比较困难时才应该使用,原因是执行绪可以配置资源,如记忆体等。如果当执行绪终止时没有释放所配置的记忆体,那么记忆体将仍然是被配置了的。执行绪不是程序:所配置的资源在一个程序的所有执行绪之间是共用的,因此当执行绪终止时,资源不会被自动释放。好的程式结构要求一个执行绪释放由它配置的所有资源。

内容来自dedecms

您还应该知道当第二个执行绪仍在执行时,可以建立第三个执行绪。如果WindowsSendMessage呼叫和_endthread呼叫之间,将控制权从第二个执行绪切换到第一个执行绪,那么视窗讯息处理程式就可能回应滑鼠按键而建立一个新的执行绪,从而出现了上述的情况。这不是什么问题,但是如果这对您自己的应用来说是一个问题的话,那么您可能会考虑使用临界区域来避免执行绪之间的冲突。 内容来自dedecms

事件物件
 

织梦内容管理系统

BIGJOB1在每次需要执行暴力测试计算时,就建立一个执行绪。执行绪在完成计算之後自动终止。 织梦好,好织梦

另一种可用的方法是在程式的整个生命周期内保持执行绪的执行,但是只在必要时才启动它。这是一个应用事件物件的理想情况。 dedecms.com

事件物件可以是「有信号的」(也称为「被设立的」)或「没信号的」(也称为「被重置的」)。您可以通过下面呼叫来建立事件物件:

本文来自织梦

hEvent = CreateEvent (&sa, fManual, fInitial, pszName) ; 内容来自dedecms 

第一个参数(指向一个SECURITY_ATTRIBUTES结构的指标)和最後一个参数(一个事件物件的名字)只有在事件物件被多个程序共用时才有意义。在同一程序中,这些参数通常被设定为NULL。如果您希望事件物件被初始化为有信号的,那么将fInitial参数设定为TRUE。而如果希望事件物件被初始化为无信号的,则将fInitial参数设定为FALSE。稍後,我将简短地描述fManual参数。

织梦内容管理系统

要设立一个现存的事件物件,呼叫 本文来自织梦

SetEvent (hEvent) ; 

copyright dedecms

要重置一个事件物件,呼叫 dedecms.com

ResetEvent (hEvent) ; 
织梦好,好织梦

一个程式通常呼叫:

内容来自dedecms

WaitForSingleObject (hEvent, dwTimeOut) ; 

内容来自dedecms

并且将第二个参数设定为INFINITE。如果事件物件目前是被设立的,那么函式将立即传回,否则,函式将暂停执行绪直到事件物件被设立。如果您将第二个参数设定为一个以毫秒计的超时时间值,这样函式也可能在事件物件被设立之前传回。 copyright dedecms

如果最初的CreateEvent呼叫的fManual参数被设定为FALSE,那么事件物件将在WaitForSingleObject函式传回时自动重置。这种功能特性通常使得事件物件没有必要使用ResetEvent函式。 织梦内容管理系统

现在,我们可以来看一看程式20-5所示的BIGJOB2.C程式。 本文来自织梦

 程式20-5  BIGJOB2 
内容来自dedecms
BIGJOB2.C 

本文来自织梦

/*---------------------------------------------------------------------------- 
copyright dedecms
        BIGJOB2.C -- Multithreading Demo copyright dedecms 
                                                             (c) Charles Petzold, 1998 
内容来自dedecms
-----------------------------------------------------------------------------*/ dedecms.com 
  织梦内容管理系统 
#include <windows.h> 

内容来自dedecms

#include <math.h> 

本文来自织梦

#include <process.h> copyright dedecms 
  织梦好,好织梦 
#define REP                                                          1000000 
内容来自dedecms
  

织梦好,好织梦

#define STATUS_READY                          0 织梦好,好织梦 
#define STATUS_WORKING                    1 

dedecms.com

#define STATUS_DONE                           2 
织梦内容管理系统
  内容来自dedecms 
#define WM_CALC_DONE                          (WM_USER + 0) 

织梦内容管理系统

#define WM_CALC_ABORTED                   (WM_USER + 1) 

copyright dedecms

  copyright dedecms 
typedef struct dedecms.com 
{ 

内容来自dedecms

        HWND                   hwnd ; 内容来自dedecms 
        HANDLE             hEvent ; 

dedecms.com

        BOOL                   bContinue ; dedecms.com 
} 
织梦内容管理系统
PARAMS, *PPARAMS ; 
织梦内容管理系统
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; copyright dedecms 
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, copyright dedecms 
                    PSTR szCmdLine, int iCmdShow) 

织梦内容管理系统

{ dedecms.com 
        static TCHAR   szAppName[] = TEXT ("BigJob2") ; 织梦好,好织梦 
        HWND                                  hwnd ; copyright dedecms 
        MSG                                   msg ; 内容来自dedecms 
        WNDCLASS                              wndclass ; 织梦内容管理系统 
  

内容来自dedecms

        wndclass.style                        = CS_HREDRAW | CS_VREDRAW ; 本文来自织梦 
        wndclass.lpfnWndProc                  = WndProc ; 
dedecms.com
        wndclass.cbClsExtra                   = 0 ; 

织梦好,好织梦

        wndclass.cbWndExtra                   = 0 ; 

本文来自织梦

        wndclass.hInstance            = hInstance ; dedecms.com 
        wndclass.hIcon                 = LoadIcon (NULL, IDI_APPLICATION) ; dedecms.com 
        wndclass.hCursor               = LoadCursor (NULL, IDC_ARROW) ; 织梦好,好织梦 
        wndclass.hbrBackground         = (HBRUSH) GetStockObject (WHITE_BRUSH) ; 
织梦内容管理系统
        wndclass.lpszMenuName          = NULL ; 

内容来自dedecms

        wndclass.lpszClassName         = szAppName ; 织梦好,好织梦 
      
本文来自织梦
        if (!RegisterClass (&wndclass)) dedecms.com 
        { dedecms.com 
       MessageBox (    NULL, TEXT ("This program requires Windows NT!"), 织梦内容管理系统 
                    szAppName, MB_ICONERROR) ; 内容来自dedecms 
                       return 0 ; 

本文来自织梦

     } 内容来自dedecms 
      dedecms.com 
        hwnd = CreateWindow (  szAppName, TEXT ("Multithreading Demo"), copyright dedecms 
                                      WS_OVERLAPPEDWINDOW, 织梦内容管理系统 
                                      CW_USEDEFAULT, CW_USEDEFAULT, 

织梦内容管理系统

                                      CW_USEDEFAULT, CW_USEDEFAULT, 内容来自dedecms 
                                      NULL, NULL, hInstance, NULL) ; 本文来自织梦 
      

织梦好,好织梦

        ShowWindow (hwnd, iCmdShow) ; 织梦内容管理系统 
        UpdateWindow (hwnd) ; copyright dedecms 
      织梦内容管理系统 
        while (GetMessage (&msg, NULL, 0, 0)) copyright dedecms 
        { 

本文来自织梦

                       TranslateMessage (&msg) ; 本文来自织梦 
                       DispatchMessage (&msg) ; copyright dedecms 
        } 织梦好,好织梦 
        return msg.wParam ; 织梦好,好织梦 
} 内容来自dedecms 
  
内容来自dedecms
void Thread (PVOID pvoid) 织梦好,好织梦 
{ copyright dedecms 
        double                 A = 1.0 ; 

织梦内容管理系统

        INT                            i ; 本文来自织梦 
        LONG                           lTime ; 
织梦内容管理系统
        volatile               PPARAMS pparams ; 
copyright dedecms
      

织梦内容管理系统

        pparams = (PPARAMS) pvoid ; 
织梦内容管理系统
        while (TRUE) 本文来自织梦 
        { 
本文来自织梦
                       WaitForSingleObject (pparams->hEvent, INFINITE) ; 
织梦内容管理系统
                       lTime = GetCurrentTime () ; 

本文来自织梦

                       for (i = 0 ; i < REP && pparams->bContinue ; i++) dedecms.com 
                                      A = tan (atan (exp (log (sqrt (A * A))))) + 1.0 ; 

内容来自dedecms

                       if (i == REP) 内容来自dedecms 
                       { 

织梦好,好织梦

                                      lTime = GetCurrentTime () - lTime ; 织梦好,好织梦 
                                      PostMessage (pparams->hwnd, WM_CALC_DONE, 0, lTime) ; 织梦好,好织梦 
                       } 织梦内容管理系统 
                       else 
copyright dedecms
                                      PostMessage (pparams->hwnd, WM_CALC_ABORTED, 0, 0) ; copyright dedecms 
        } 
copyright dedecms
} 内容来自dedecms 
  

copyright dedecms

LRESULT CALLBACK WndProc (     HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) copyright dedecms 
{ dedecms.com 
        static HANDLE         hEvent ; 本文来自织梦 
        static INT            iStatus ; 
本文来自织梦
        static LONG           lTime ; 织梦内容管理系统 
        static PARAMS params ; copyright dedecms 
        static         TCHAR *szMessage[] = { TEXT ("Ready (left mouse button begins)"), 
本文来自织梦
                    TEXT ("Working (right mouse button ends)"), 

织梦内容管理系统

                    TEXT ("%d repetitions in %ld msec") } ; 织梦好,好织梦 
        HDC                                           hdc ; dedecms.com 
        PAINTSTRUCT                            ps ; 本文来自织梦 
        RECT                                          rect ; 
dedecms.com
        TCHAR                                         szBuffer[64] ; 
内容来自dedecms
      本文来自织梦 
        switch (message) 
织梦内容管理系统
        { 

copyright dedecms

        case    WM_CREATE: copyright dedecms 
                               hEvent = CreateEvent (NULL, FALSE, FALSE, NULL) ; 

织梦好,好织梦

           织梦好,好织梦 
                               params.hwnd = hwnd ; 
dedecms.com
                               params.hEvent = hEvent ; 织梦好,好织梦 
                               params.bContinue = FALSE ; dedecms.com 
           
织梦好,好织梦
                               _beginthread (Thread, 0, ¶ms) ; 
copyright dedecms
           copyright dedecms 
                               return 0 ; 
织梦好,好织梦
           内容来自dedecms 
        case    WM_LBUTTONDOWN: 织梦好,好织梦 
                               if (iStatus == STATUS_WORKING) 
内容来自dedecms
                               { 

织梦内容管理系统

                                      MessageBeep (0) ; copyright dedecms 
                                      return 0 ; 

本文来自织梦

                               } 
织梦好,好织梦
                               iStatus = STATUS_WORKING ; 
copyright dedecms
                               params.bContinue = TRUE ; 

copyright dedecms

           

织梦内容管理系统

                               SetEvent (hEvent) ; copyright dedecms 
         
内容来自dedecms
                               InvalidateRect (hwnd, NULL, TRUE) ; dedecms.com 
                               return 0 ; 织梦好,好织梦 
           
内容来自dedecms
        case    WM_RBUTTONDOWN: copyright dedecms 
                               params.bContinue = FALSE ; 

dedecms.com

                               return 0 ; 内容来自dedecms 
           

织梦好,好织梦

        case    WM_CALC_DONE: dedecms.com 
                               lTime = lParam ; dedecms.com 
                               iStatus = STATUS_DONE ; 内容来自dedecms 
                               InvalidateRect (hwnd, NULL, TRUE) ; dedecms.com 
                               return 0 ; 
copyright dedecms
           copyright dedecms 
        case    WM_CALC_ABORTED: copyright dedecms 
                               iStatus = STATUS_READY ; 
内容来自dedecms
                               InvalidateRect (hwnd, NULL, TRUE) ; 织梦好,好织梦 
                               return 0 ; 内容来自dedecms 
           织梦好,好织梦 
        case    WM_PAINT: 织梦内容管理系统 
                               hdc = BeginPaint (hwnd, &ps) ; 

dedecms.com

           本文来自织梦 
                               GetClientRect (hwnd, &rect) ; 内容来自dedecms 
           织梦好,好织梦 
                               wsprintf (     szBuffer, szMessage[iStatus], REP, lTime) ; 

dedecms.com

                               DrawText (     hdc, szBuffer, -1, &rect, 
织梦内容管理系统
                                                                     DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; 
织梦好,好织梦
           织梦好,好织梦 
                               EndPaint (hwnd, &ps) ; dedecms.com 
                               return 0 ; 织梦好,好织梦 
           

本文来自织梦

        case    WM_DESTROY: 

织梦内容管理系统

                               PostQuitMessage (0) ; 织梦好,好织梦 
                               return 0 ; dedecms.com 
        } 织梦内容管理系统 
        return DefWindowProc (hwnd, message, wParam, lParam) ; 
本文来自织梦
} 
本文来自织梦

处理WM_CREATE讯息时,视窗讯息处理程式首先建立一个初始化为没信号的自动重置事件物件,然後建立执行绪。

dedecms.com

Thread函式进入一个无限的while回圈,在回圈开始时首先呼叫WaitForSingleObject(注意PARAMS结构包括一个包含事件物件代号的栏位)。因为事件被初始化为重置的,所以执行绪的执行被阻挡在函式呼叫中。按下滑鼠左键将导致视窗程序呼叫SetEvent,这将释放由WaitForSingleObject呼叫产生的第二个执行绪,并开始暴力测试计算。当计算完之後,执行绪再次呼叫WaitForSingleObject,但是由於第一次呼叫已经使事件物件重置,因此,执行绪将被暂停,直到再次按下滑鼠。 本文来自织梦

在其他方面,程式几乎和BIGJOB1完全一样。

织梦好,好织梦

执行绪区域储存空间(TLS
 

dedecms.com

多执行绪程式中的整体变数(以及任何被配置的记忆体)被程式中的所有执行绪共用。在一个函式中的局部静态变数也被使用函式的所有执行绪共用。一个函式中的局部动态变数是唯一於各个执行绪的,因为它们被储存在堆叠上,而每个执行绪有它自己的堆叠。 织梦内容管理系统

对各个执行绪唯一的持续性储存空间有存在的必要。例如,我在本章前面提到过的C中的strtok函式要求这种型态的储存空间。不幸的是,C语言不支援这类储存空间。但是Windows中提供了四个函式,它们实作了一种技术来做到这一点,并且MicrosoftC的扩充语法也支援它,这就叫做执行绪区域储存空间。

内容来自dedecms

下面是API工作的方法:

织梦好,好织梦

首先,定义一个包含需要唯一於执行绪的所有资料的结构,例如: 内容来自dedecms

typedef struct 
dedecms.com
{ 内容来自dedecms 
        int a ; 本文来自织梦 
        int b ; dedecms.com 
} 
内容来自dedecms
DATA, * PDATA ; 织梦内容管理系统 

主执行绪呼叫TlsAlloc获得一个索引值:

织梦好,好织梦

dwTlsIndex = TlsAlloc () ; 
内容来自dedecms

这个值可以储存在一个整体变数中或者通过参数结构传递给执行绪函式。

织梦内容管理系统

执行绪函式首先为该资料结构配置记忆体,并使用上面所获得的索引值呼叫TlsSetValue

织梦好,好织梦

TlsSetValue (dwTlsIndex, GlobalAlloc (GPTR, sizeof (DATA)) ; dedecms.com 

该函式将一个指标和某个执行绪及某个执行绪索引相关联。现在,任何需要使用这个指标的函式(包括最初的执行绪函式本身)都可以包含如下所示的程式码: 织梦内容管理系统

PDATA pdata ; 

织梦好,好织梦

... 织梦内容管理系统 
pdata = (PDATA) TlsGetValue (dwTlsIndex) ; 织梦好,好织梦 

现在函式可以设定或者使用pdata->apdata->b了。在执行绪函式终止以前,它释放配置的记忆体:

copyright dedecms

GlobalFree (TlsGetValue (dwTlsIndex)) ; 
织梦内容管理系统

当使用该资料的所有执行绪都终止之时,主执行绪将释放索引: copyright dedecms

TlsFree (dwTlsIndex) ; 

dedecms.com

这个程序刚开始可能令人有些迷惑,因此如果能看一看如何实作执行绪区域储存空间可能会有帮助(我不知道Windows实际上是如何实作的,但下面的方案是可能的)。首先,TlsAlloc可能只是配置一块记忆体(长度为0)并传回一个索引值,即指向这块记忆体的一个指标。每次使用该索引呼叫TlsSetValue时,通过重新配置将记忆体块增大8个位元组。在这8个位元组中储存的是呼叫函式的执行绪ID(通过GetCurrentThreadId来获得)以及传递给TlsSetValue函式的指标。TlsSetValue简单地使用执行绪ID来搜寻作业系统管理的执行绪区域储存空间位址表,然後传回指标。TlsFree将释放记忆体块。所以您看,这可能是一件容易得可以由您自己来实作的事情。不过,既然已经有工具为您做好了这些工作,那也不错。 本文来自织梦

MicrosoftC的扩充功能使这件工作更加容易。只要在要对每个执行绪都保留不同内容的变数前加上__declspec (thread)就好了。对於任何函式的外部静态变数,则为: copyright dedecms

__declspec (thread) int iGlobal = 1 ; 

内容来自dedecms

对於函式内部的静态变数,则为:

dedecms.com

__declspec (thread) static int iLocal = 2 ; 内容来自dedecms 

  本文来自织梦