八宝书库 > 文学其他电子书 > 深入浅出MFC第2版(PDF格式) >

第19部分

深入浅出MFC第2版(PDF格式)-第19部分

小说: 深入浅出MFC第2版(PDF格式) 字数: 每页4000字

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!




                    _ 

常数中比较常用的是                     ,它会使得子进程产生之后,其主执行线程立 



刻被暂停执行。 



第七个参数lpE nvironment 可以指定进程所使用的环境变量区。通常我们会让子进程继 



承父进程的环境变量,那么这里要指定NULL 。 



第八个参数lpCur rentDirectory 用来设定子进程的工作目录与工作磁盘。如果指定 



NULL ,子进程就会使用父进程的工作目录与工作磁盘。 



第九个参数lpSt artupInfo 是一个指向STAR TUPINF O 结构的指针。这是一个庞大的结 



构,可以用来设定窗口的标题、位置与大小,详情请看API 使用手册。 



                  PROCESS  INF ORMA TION  

                        _ 

最后一个参数是一个指向                         结构的指针: 



                                                                  42 


…………………………………………………………Page 105……………………………………………………………

        typedef struct _PROCESS_INFORMATION { 



               HANDLE hProcess; 



               HANDLE hThread; 



               DWORD dwProcessId; 



               DWORD dwThreadId; 



       } PROCESS_INFORMATION; 



当系统为我们产生「进程对象」和「执行线程对象」,它会把两个对象的handle 填入此结 



构的相关字段中,应用程序可以从这里获得这些handles 。 



如果一个进程想结束自己的生命,只要调用: 



       VOID ExitProcess(UINT fuExitCode); 



就可以了。如果进程想结束另一个进程的生命,可以使用: 



       BOOL TerminateProcess(HANDLE hProcess; UINT fuExitCode); 



很显然,只要你有某个进程的handle,就可以结束它的生命。TerminateProcess 并不被 



建议使用,倒不是因为它的权力太大,而是因为一般进程结束时,系统会通知该进程所 



开启(所使用)的所有DLLs,但如果你以TerminateProcess 结束一个进程,系统不会 



做这件事,而这恐怕不是你所希望的。 



前面我曾说过所谓割断脐带这件事情,只要你把子进程以CloseHandle 关闭,就达到了 



目的。下面是个例子: 



      PROCESS_INFORMATION ProcInfo; 



      BOOL fSuccess; 



      fSuccess = CreateProcess(。。。;&ProcInfo); 



      if  (fSuccess) { 



               CloseHandle (ProcInfo。hThread); 



               CloseHandle (ProcInfo。hProcess); 



      } 



                                                                                43 


…………………………………………………………Page 106……………………………………………………………

一个执行线程的诞生与死亡 



    程序代码的执行,是执行线程的工作。当一个进程建立起来,主执行线程也产生。所以每一个 



    Windows 程序一开始就有了一个执行线程。我们可以调用CreateThread 产生额外的执行 



    线程,系统会帮我们完成下列事情: 



    1。 配置「执行线程对象」,其handle 将成为CreateThread  的传回值。 



                 1 

    2。 设定计数值为 。 



    3。 配置执行线程的context 。 



    4。 保留执行线程的堆栈。 



        context                SS                 IP 

    5。 将     中的堆栈指针缓存器(          )和指令指针缓存器(  )设定妥当。 



    看看上面的态势,的确可以显示出执行线程是CPU 分配时间的单位。所谓工作切换(context 



    switch )其实就是对执行线程的context  的切换。 



    程序若欲产生一个新执行线程,调用CreateThread  即可办到: 



        CreateThread (LPSECURITY_ATTRIBUTES lpThreadAttributes; 



                  DWORD dwStackSize; 



                  LPTHREAD_START_ROUTINE lpStartAddress; 



                  LPVOID lpParameter; 



                  DWORD dwCreationFlags; 



                  LPDWORD lpThreadId 



                  ); 



    第一个参数表示安全属性的设定以及继承,请参考API 手册。Windows 95 忽略此一参 



    数。第二个参数设定堆栈的大小。第三个参数设定「执行线程函数」名称,而该函数的参 



                                           0 

    数则在这里的第四个参数设定。第五个参数如果是 ,表示让执行线程立刻开始执行,如 



       CREA TE SUSPENDED  

              _ 

    果是                   , 则是要求执行线程暂停执行( 那么我们必须调用 

                                                     



    ResumeThread 才能令其重新开始)。最后一个参数是个指向D WORD  的指针,系统会 



    把执行线程的ID 放在这里。 



    上面我所说的「执行线程函数」是什么?让我们看个实例: 



                                                                             44 


…………………………………………………………Page 107……………………………………………………………

VOID ReadTime(VOID); 

HANDLE hThread; 

DWORD ThreadID; 



hThread = CreateThread(NULL; 0; (LPTHREAD_START_ROUTINE)ReadTime; 

                           NULL; 0; &ThreadID); 

。。。 

//…………………………………………………………………………………………………………………………………………………………………………………

// thread 函数 。 

// 不断利用 GetSystemTime 取系统时间 , 

// 并将结果显示在对话框 _hhwndDlg 的 IDE_TIMER 栏位上 。 

//…………………………………………………………………………………………………………………………………………………………………………………

VOID ReadTime (VOID) 

{ 

char str'50'; 

SYSTEMTIME st; 



    while(1) { 

        GetSystemTime(&st); 

        sprintf(str;〃%u:%u:%u〃; st。wHour; st。wMinute; st。wSecond); 

        SetDlgItemText (_hWndDlg; IDE_TIMER; str); 

        Sleep  (1000);  // 延迟一秒 。 

    } 

} 



当CreateThread 成功,系统为我们把一个执行线程该有的东西都准备好。执行线程的主体在 



哪里呢?就在所谓的执行线程函数。执行线程与执行线程之间,不必考虑控制权释放的问题, 



因为Win32 操作系统是强制性多任务。 



执行线程的结束有两种情况,一种是寿终正寝,一种是未得善终。前者是执行线程函数正常 



结束退出,那么执行线程也就自然而然终结了。这时候系统会调用ExitThread 做些善后清 



理工作(其实执行线程中也可以自行调用此函数以结束自己)。但是像上面那个例子,执 



行线程根本是个无穷循环,如何终结?一者是进程结束(自然也就导至执行线程的结束), 



二者是别的执行线程强制以TerminateThread 将它终结掉。不过,TerminateThread 太过毒 



辣,非必要还是少用为妙(请参考API 手册)。 



                                                                                   45 


…………………………………………………………Page 108……………………………………………………………

以_beginthreadex 取代CreateThread 



    别忘了Windows 程序除了调用Win32 API ,通常也很难避免调用任何一个C runtime  函 



    数。为了保证多线程情况下的安全,C runtime  函数库必须为每一个执行线程做一些簿记工 



    作。没有这些工作,C runtime  函数库就不知道要为每一个执行线程配置一块新的内存, 



    做为执行线程的区域变量用。因此,CreateThread 有一个名为_beginthreadex  的外包函数, 



    负责额外的簿记工作。 



    请注意函数名称的底线符号。它必须存在,因为这不是个标准的ANSI C runtime  函数。 



    _beginthreadex  的参数和CreateThread  的参数其实完全相同,不过其型别已经被「净化」 



    了,不再有Win32 型别包装。这原本是为了要让这个函数能够移植到其它操作系统,因 



    为微软希望_beginthreadex 能够被实作于其它平台,不需要和Windows 有关、不需要 



    包含windows。h 。但实际情况是,你还是得调用CloseHandle  以关闭执行线程, 而 

                                                                    



    CloseHandle 却是个Win32 API ,所以你还是需要包含windows。h 、还是和Windows 脱 



    离不了关系。微软空有一个好主意,却没能落实它。 



    把_beginthreadex 视为CreateThread  的一个看起来比较有趣的版本,就对了: 



        unsigned long _beginthreadex ( 



                         void *security; 



                         unsigned stack_size; 



                         unsigned  (__stdcall *start_address)(void *); 



                         void *arglist; 



                         unsigned initflag; 



                         unsigned* thrdaddr 



                          ); 



    _beginthreadex 所传回的unsigned long 事实上就是一个Win32 HANDLE ,指向新执行 



    线程。换句话说传回值和CreateThread 相同,但_beginthreadex 另外还设立了errno 和 



    doserrno 。 



    下面是一个最简单的使用范例: 



     #0001 #include  



     #0002  #include  



                                                                                46 


…………………………………………………………Page 109……………………………………………………………

#0003  unsigned __stdcall myfunc(void* p); 

#0004 

#0005  void main() 

#0006  { 

#0007      unsigned long thd; 

#0008      unsigned tid; 

#0009 

#0010      thd = _beginthreadex(NULL; 

#0011                           0; 

#0012                           myfunc; 

#0013                           0; 

#0014                           0; 

#0015                           &tid ); 

#0016      if (thd != NULL) 

#0017      { 

#0018          CloseHandle(thd); 

#0019      } 

#0020  } 

#0021 

#0022  unsigned __stdcall myfunc (void* p) 

#0023  { 

#0024      // do your job。。。 

#0025  } 



针对Win32 API ExitThread ,也有一个对应的C runtime  函数:_endthreadex 。它只需要 



一个参数,就是由_beginthreadex 第6个参数传回来的ID 值。 



关于_beginthreadex 和_endthreadex ,以及执行线程的其它各种理论基础、程序技术、使 



用技巧,可参考由Jim Beveridge & Robert Wiener 合着,Addison Wesley  出版的 



Multithreading Applications in  Win32     Win32                 /         /  

                                    一书(          多线程程序设计 侯俊杰译 峰出 



版)。 



                                                                                               47 


…………………………………………………………Page 110……………………………………………………………

执行线程优先权 (                   y) 

                      Priorit 



  优先权是排程的重要依据。优先权高的执行线程,永远先获得CPU  的青睐。当然啦,操作 



  系统会视情况调整各个执行线程的优先权。例如前景执行线程的优先权应该调高一些,背 



  景执行线程的优先权应该调低一些。 



                       0         31 

  执行线程的优先权范围从            (最低)到  (最高)。当你产生执行线程,并不是直接以数值 



  指定其优先权,而是采用两个步骤。第一个步骤是指定「优先权等级(Priority Class )」 



  给进程,第二步骤是指定「相对优先权」给该进程所拥有的执行线程。图1…7 是优先权等 



  级的描述,其中的代码在CreateProcess  的dwCreationFlags 参数中指定。如果你不指 



                    NORMAL  PRIORIT Y  CLASS … 

                           _        _ 

  定, 系统预设给的是                                 除非父进程是 



  IDLE  PRIORIT Y  CLASS              I

返回目录 上一页 下一页 回到顶部 0 0

你可能喜欢的