Windows程序设计第8章进程与线程.ppt

上传人:本田雅阁 文档编号:3486398 上传时间:2019-09-02 格式:PPT 页数:21 大小:109.05KB
返回 下载 相关 举报
Windows程序设计第8章进程与线程.ppt_第1页
第1页 / 共21页
Windows程序设计第8章进程与线程.ppt_第2页
第2页 / 共21页
Windows程序设计第8章进程与线程.ppt_第3页
第3页 / 共21页
Windows程序设计第8章进程与线程.ppt_第4页
第4页 / 共21页
Windows程序设计第8章进程与线程.ppt_第5页
第5页 / 共21页
点击查看更多>>
资源描述

《Windows程序设计第8章进程与线程.ppt》由会员分享,可在线阅读,更多相关《Windows程序设计第8章进程与线程.ppt(21页珍藏版)》请在三一文库上搜索。

1、第8章 进程与线程,Win32操作系统平台提供了强大的多任务功能,其中 “进程”(Process)和“线程”(Thread)是其控制多任务的两个重要概念。早期的Windows 3.x只能依靠应用程序之间的协同来实现协同式多任务,而Windows 95/NT实行的是抢占式多任务。 在Win 32(Windows 95/NT)中,每一个进程可以同时执行多个线程,这意味着一个程序可以同时完成多个任务。对于象通信应用程序那样的既要进行耗时的工作,又要保持对用户输入响应的应用来说,使用多线程是最佳选择。当进程使用多个线程时,需要采取适当的措施来保持线程间的同步。,进程与子进程 进程(process)是计

2、算机操作系统中的概念。进程可以理解为:程序在给定的初始状态和内存区域中,能共行执行的一次“计算”,亦即,进程是可以和其它程序共行执行的,程序的一次执行。进程有独自的内存空间、程序代码、信息以及一堆大大小小的系统资源。另外,进程之间也有父子关系,产生进程的进程是“父进程”,被产生的进程是“子进程”,通常父进程对子进程有控制权,同时子进程也可以存取父进程的资源。,创建子进程API函数 下面介绍一个很重要的函数,即CreateProcess函数,其原型为: BOOL CreateProcess(LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSE

3、CURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTURINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation); CreateProcess() 的功能是建立并执行child process。,其它函数 本节将介绍

4、一些可以协助我们取得process相关信息的函数。第一组要介绍的是最重要的GetCurentProcess()GetCurrentProcessId(), 前者会返回目前正在执行的process(也就是调用者)的handle, 后者会返回调用者的id。Handle和id有什么不同呢?id只是一个数字, Win32保证不会有第二个在系统中执行的process拥有相同的id, 因此,这个数字通常用来鉴别process的身份; process handle可就重要了, 因为其他与process有关的函数都需要它来当参数。 l HANDLE GetCurrentProcess(VOID); l DWO

5、RD GetCurrentProcessId(VOID); DWORD GetPriorityClass(HANDLE hProcess); BOOL SetPriorityClass(HANDLE hProcess, DWORD dwPriorityClass);,结束process 如果某个process想停止执行, 可调用ExitProcess(), 不过我们通常不直接调用它, 而是调用C程序库中的exit(), exit()在自动执行一些清除垃圾的工作之后, 再调用ExitProcess()。 VOID ExitProcess(UNT uExitCode); 不过, 如果process

6、 A 想要迫使process B 停止执行, 可以在取得process B 的handle 之后, 调用TerminateProcess(): BOOL TerminateProcess(HANDLE hProcess, UNIT uExitCode);,进程与线程 前一节我们讨论了进程和子进程的有关概念和编程技术,实际上在Windows 操作系统环境中,Microsoft提出了线程是更具有挑战性的编程概念。在Windows 32位操作系统中,所谓多任务是指系统可以同时运行多个进程,而每个进程也可以同时执行多个线程。所谓进程就是应用程序的运行实例。一个进程又可以分为多个线程,根据线程的运行特征

7、,我们可以把它看成是操作系统分配CPU时间的基本实体。,Win16 的协同多任务 早在16位的Windows中,应用程序具有对CPU的控制权。只有在调用了GetMessage、PeekMessage、WaitMessage或Yield后,程序才有可能把CPU控制权交给系统,系统再把控制权转交给别的应用程序。如果应用程序在长时间内无法调用上述四个函数之一,那么程序就一直独占CPU,系统会被挂起而无法接受用户的输入。 有人可能会想到用CWinApp:OnIdle函数来执行后台工作,因为该函数是程序主消息循环在空闲时调用的。但OnIdle的执行并不可靠,例如,如果用户在程序中打开了一个菜单或模态对话

8、框,那么OnIdle将停止调用,因为此时程序不能返回到主消息循环中!在实时任务代码中调用PeekMessage也会遇到同样的问题。 折衷的办法是在执行长期工作时弹出一个非模态对话框并禁止主窗口,在消息循环内分批执行后台操作。对话框中可以显示工作的进度,也可以包含一个取消按钮以让用户有机会中断一个长期的工作。典型的代码如程序8.1所示。这样做既可以保证工作实时进行,又可以使程序能有限地响应用户输入,但此时程序实际上已不能再为用户干别的事情了。,Windows 95/NT的抢先式多任务 在32位的Windows系统中,采用的是抢先式多任务,这意味着程序对CPU的占用时间是由系统决定的。系统为每个程

9、序分配一定的CPU时间,当程序的运行超过规定时间后,系统就会中断该程序并把CPU控制权转交给别的程序。与协同式多任务不同,这种中断是汇编语言级的。程序不必调用象PeekMessage这样的函数来放弃对CPU的控制权,就可以进行费时的工作,而且不会导致系统的挂起。 例如,在Windows3.x 中,如果某一个应用程序陷入了死循环,那么整个系统都会瘫痪,这时唯一的解决办法就是重新启动机器。而在Windows 95/NT中,一个程序的崩溃一般不会造成死机,其它程序仍然可以运行,用户可以按Ctrl+Alt+Del键来打开任务列表并关闭没有响应的程序。,进程与线程 所谓进程就是应用程序的运行实例。每个进

10、程都有自己私有的虚拟地址空间。每个进程都有一个主线程,但可以建立另外的线程。进程中的线程是并行执行的,每个线程占用CPU的时间由系统来划分。 进程中的所有线程共享进程的虚拟地址空间,这意味着所有线程都可以访问进程的全局变量和资源。这一方面为编程带来了方便,但另一方面也容易造成冲突。 虽然在进程中进行费时的工作不会导致系统的挂起,但这会导致进程本身的挂起。所以,如果进程既要进行长期的工作,又要响应用户的输入,那么它可以启动一个线程来专门负责费时的工作,而主线程仍然可以与用户进行交互。,线程的创建和终止 线程分用户界面线程和工作者线程两种。用户界面线程拥有自己的消息泵来处理界面消息,可以与用户进行

11、交互。工作者线程没有消息泵,一般用来完成后台工作。 MFC应用程序的线程由对象CWinThread表示。在多数情况下,程序不需要自己创建CWinThread对象。调用AfxBeginThread函数时会自动创建一个CWinThread对象。 当发生下列事件之一时,线程被终止: l 线程调用ExitThread; l 线程函数返回,即线程隐含调用了ExitThread; l ExitProcess被进程的任一线程显式或隐含调用; l 用线程的句柄调用TerminateThread; l 用进程句柄调用TerminateProcess。,线程的同步 多线程的使用会产生一些新的问题,主要是如何保证线

12、程的同步执行。多线程应用程序需要使用同步对象和等待函数来实现同步。同步问题是实现远程数据采集或远程自动控制编程中的关键技术问题。,为什么需要同步 由于同一进程的所有线程共享进程的虚拟地址空间,并且线程的中断是汇编语言级的,所以可能会发生两个线程同时访问同一个对象(包括全局变量、共享资源、API函数和MFC对象等)的情况,这有可能导致程序错误。例如,如果一个线程在未完成对某一大尺寸全局变量的读操作时,另一个线程又对该变量进行了写操作,那么第一个线程读入的变量值可能是一种修改过程中的不稳定值。 属于不同进程的线程在同时访问同一内存区域或共享资源时,也会存在同样的问题。 因此,在多线程应用程序中,常

13、常需要采取一些措施来同步线程的执行。,等待函数 Win32 API提供了一组能使线程阻塞其自身执行的等待函数。这些函数只有在作为其参数的一个或多个同步对象(见下小节)产生信号时才会返回。在超过规定的等待时间后,不管有无信号,函数也都会返回。在等待函数未返回时,线程处于等待状态,此时线程只消耗很少的CPU时间。 使用等待函数即可以保证线程的同步,又可以提高程序的运行效率。最常用的等待函数是WaitForSingleObject,该函数的声明为: DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); 参数hHandle是同

14、步对象的句柄。参数dwMilliseconds是以毫秒为单位的超时间隔,如果该参数为0,那么函数就测试同步对象的状态并立即返回,如果该参数为INFINITE,则超时间隔是无限的。,同步对象 同步对象用来协调多线程的执行,它可以被多个线程共享。线程的等待函数用同步对象的句柄作为参数,同步对象应该是所有要使用的线程都能访问到的。同步对象的状态要么是有信号的,要么是无信号的。同步对象主要有三种:事件、mutex和信号灯。 事件对象(Event)是最简单的同步对象,它包括有信号和无信号两种状态。在线程访问某一资源之前,也许需要等待某一事件的发生,这时用事件对象最合适。例如,只有在通信端口缓冲区收到数据

15、后,监视线程才被激活。 mutex对象的状态在它不被任何线程拥有时是有信号的,而当它被拥有时则是无信号的。mutex对象很适合用来协调多个线程对共享资源的互斥访问(mutually exclusive)。 信号灯对象维护一个从0开始的计数,在计数值大于0时对象是有信号的,而在计数值为0时则是无信号的。信号灯对象可用来限制对共享资源进行访问的线程数量。线程用CreateSemaphore函数来建立信号灯对象,在调用该函数时,可以指定对象的初始计数和最大计数。在建立信号灯时也可以为对象起个名字,别的进程中的线程可以用OpenSemaphore函数打开指定名字的信号灯句柄。,关键节和互锁变量访问 关

16、键节(Critical Seciton)与mutex的功能类似,但它只能由同一进程中的线程使用。关键节可以防止共享资源被同时访问。 进程负责为关键节分配内存空间,关键节实际上是一个CRITICAL_SECTION型的变量,它一次只能被一个线程拥有。在线程使用关键节之前,必须调用InitializeCriticalSection函数将其初始化。如果线程中有一段关键的代码不希望被别的线程中断,那么可以调用EnterCriticalSection函数来申请关键节的所有权,在运行完关键代码后再用LeaveCriticalSection函数来释放所有权。如果在调用EnterCriticalSection

17、时关键节对象已被另一个线程拥有,那么该函数将无限期等待所有权。 利用互锁变量可以建立简单有效的同步机制。使用函数InterlockedIncrement和InterlockedDecrement可以增加或减少多个线程共享的一个32位变量的值,并且可以检查结果是否为0。线程不必担心会被其它线程中断而导致错误。如果变量位于共享内存中,那么不同进程中的线程也可以使用这种机制。,串行通信与重叠I/O Win 32系统为串行通信提供了全新的服务。传统的OpenComm、ReadComm、WriteComm、CloseComm等函数已经过时,WM_COMMNOTIFY消息也消失了。取而代之的是文件I/O函

18、数提供的打开和关闭通信资源句柄及读写操作的基本接口。 新的文件I/O函数(CreateFile、ReadFile、WriteFile等)支持重叠式输入输出,这使得线程可以从费时的I/O操作中解放出来,从而极大地提高了程序的运行效率。,串行口的打开和关闭 Win 32系统把文件的概念进行了扩展。无论是文件、通信设备、命名管道、邮件槽、磁盘、还是控制台,都是用API函数CreateFile来打开或创建的。该函数的声明为: HANDLE CreateFile( LPCTSTR lpFileName, / 文件名 DWORD dwDesiredAccess, / 访问模式 DWORD dwShareM

19、ode, / 共享模式 PSECURITY_ATTRIBUTES lpSecurityAttributes, / 通常为NULL DWORD dwCreationDistribution, / 创建方式 DWORD dwFlagsAndAttributes, / 文件属性和标志 HANDLE hTemplateFile / 临时文件的句柄,通常为NULL ); 如果调用成功,那么该函数返回文件的句柄,如果调用失败,则函数返回INVALID_HANDLE_VALUE。,串行口的初始化 在打开通信设备句柄后,常常需要对串行口进行一些初始化工作。这需要通过一个DCB结构来进行。DCB结构包含了诸如波

20、特率、每个字符的数据位数、奇偶校验和停止位数等信息。在查询或配置置串行口的属性时,都要用DCB结构来作为缓冲区。 调用GetCommState函数可以获得串口的配置,该函数把当前配置填充到一个DCB结构中。一般在用CreateFile打开串行口后,可以调用GetCommState函数来获取串行口的初始配置。要修改串行口的配置,应该先修改DCB结构,然后再调用SetCommState函数用指定的DCB结构来设置串行口。 在用ReadFile和WriteFile读写串行口时,需要考虑超时问题。如果在指定的时间内没有读出或写入指定数量的字符,那么ReadFile或WriteFile的操作就会结束。要

21、查询当前的超时设置应调用GetCommTimeouts函数,该函数会填充一个COMMTIMEOUTS结构。调用SetCommTimeouts可以用某一个COMMTIMEOUTS结构的内容来设置超时。,重叠I/O 在用ReadFile和WriteFile读写串行口时,既可以同步执行,也可以重叠(异步)执行。在同步执行时,函数直到操作完成后才返回。这意味着在同步执行时线程会被阻塞,从而导致效率下降。在重叠执行时,即使操作还未完成,调用的函数也会立即返回。费时的I/O操作在后台进行,这样线程就可以干别的事情。例如,线程可以在不同的句柄上同时执行I/O操作,甚至可以在同一句柄上同时进行读写操作。“重叠

22、”一词的含义就在于此。 ReadFile函数只要在串行口输入缓冲区中读入指定数量的字符,就算完成操作。而WriteFile函数不但要把指定数量的字符拷入到输出缓冲中,而且要等这些字符从串行口送出去后才算完成操作。 ReadFile和WriteFile函数是否为执行重叠操作是由CreateFile函数决定的。如果在调用CreateFile创建句柄时指定了FILE_FLAG_OVERLAPPED标志,那么调用ReadFile和WriteFile对该句柄进行的读写操作就是重叠的,如果未指定重叠标志,则读写操作是同步的。,通信事件 在Windows 95/NT中,WM_COMMNOTIFY消息已经取消,在串行口产生一个通信事件时,程序并不会收到通知消息。线程需要调用WaitCommEvent函数来监视发生在串行口中的各种事件,该函数的第二个参数返回一个事件屏蔽变量,用来指示事件的类型。线程可以用SetCommMask建立事件屏蔽以指定要监视的事件,表8.4列出了可以监视的事件。调用GetCommMask可以查询串行口当前的事件屏蔽。,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1