windows进程和线程.ppt_第1页
windows进程和线程.ppt_第2页
windows进程和线程.ppt_第3页
windows进程和线程.ppt_第4页
windows进程和线程.ppt_第5页
已阅读5页,还剩43页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第4章 Windows进程和线程,,主要内容:,windows进程 windows线程 线程同步,一. 进程,进程是不活泼的。若要使进程完成某项操作,它必须至少拥有一个线程,该线程负责执行包含在进程的地址空间中的代码。 实际上,单个进程可能包含若干个线程,所有这些线程都“同时”执行进程地址空间中的代码。 为此,每个线程都有它自己的一组CPU寄存器和它自己的堆栈。 每个进程至少拥有一个线程,来执行进程的地址空间中的代码。,当创建一个进程时,系统会自动创建它的第一个线程,称为主线程。然后,该线程可以创建子线程,而这些子线程又能创建更多的子线程。,大约20ms,Windows 进程操作相关API: CreateProcess():进程创建 ExitProcess()或TerminateProcess():进程退出 ExitProcess()终止一个进程和它的所有线程;它的终止操作是完整的,包括关闭所有对象句柄、它的所有线程等; TerminateProcess()终止指定的进程和它的所有线程;它的终止操作是不完整的(如:不向相关DLL通报关闭情况),通常只用于异常情况下对进程的终止。,范例:进程创建与退出,进程创建与退出,二. 线程,线程(thread):是为了提高系统内程序的并发执行程度而提出来的概念,它是比进程更小的能够独立运行的基本单位。线程是一个轻量级实体(light-weight entity),它的结构(thread structure)相对简单,在切换速度上非常得快,同一进程中的线程切换不会引起进程的切换,对于并行计算来讲,有效的利用线程能够改善计算的效率,简化计算的复杂性。,线程也是由两个部分组成的: 一个是线程的内核对象,操作系统用它来对线程实施管理。 另一个是线程堆栈,它用于维护线程在执行代码时需要的所有函数参数和局部变量。,2.1 线程组成,比较进程与线程,同一进程中的线程共享如下内容: 全局变量 堆数据 打开的文件句柄 用户及用户组ID 但每个线程具有独立的: 线程ID 堆栈 error变量 优先级,几点提示:,同一进程中的线程共享这个进程的全部资源与地址空间,这样,多个线程确实能够非常容易地互相通信。 Windows支持的是内核级线程: 由内核完成创建和撤销; 内核维护进程和线程的上下文信息; 线程切换由内核完成; 一个线程发起系统调用而阻塞,不会影响其他线程的运行。 时间片分配给线程,所以多线程的进程获得更多CPU时间。 Windows是个抢占式多线程系统,依赖优先级。,2.2 创建线程的方法与区别(针对C/C+/MFC),1、CreateThread( ) WIN32 API函数 2、_beginthreadex( ) MS对C Runtime库的扩展SDK函数 3、AfxBeginThread( ) MFC中线程创建的MFC函数,_beginthreadex( ) 初始化; CreateThread( ); ,AfxBeginThread( ) 线程对象初始化; _beginthreadex(); ,CreateThread: 提供操作系统级别的创建线程的操作,且仅限于工作者线程。线程函数中不调用MFC和RTL的函数时,可以用CreateThread,其它情况不要轻易使用,以免内存泄漏。 但它没有考虑: (1)C Runtime库里面有一些函数使用了全局量,如果使用 CreateThread 的情况下使用这些C+ 运行库的函数,就会出现不安全的问题。而 _beginthreadex 为这些全局变量做了处理(典型的例子是strtok函数) (2)MFC也需要知道新线程的创建,也需要做一些初始化工作。,_beginthreadex : MS对C Runtime库的扩展SDK函数,首先针对C Runtime库做了一些初始化的工作,以保证C Runtime库工作正常。然后,调用CreateThread真正创建线程。 仅使用Runtime Library时,可以用_BegingThread。 _beginthreadex函数在创建线程的时候分配了一个堆结构并和线程本身关联起来,这个结构叫做tiddata结构 ,线程创建时传入的线程入口函数地址就保存在这个结构中;另外,C运行时库中有些函数需要通过这个结构来保存和获取一些数据,比如说errno之类的线程全局变量。 当一个线程调用一个要求tiddata结构的C Runtime库函数的时候,将发生下面的情况: C Runtime库函数试图TlsGetvalue获取线程数据块的地址,如果没有获取到,函数就会现场分配一个 tiddata结构,并且和线程相关联,于是问题出现了,如果不通过_endthreadex函数来终结线程的话,这个结构将不会被撤销,内存泄漏就会出现了。但通常情况下,我们都不推荐使用_endthreadex函数来结束线程,因为里面包含了ExitThread调用。,AfxBeginThread : MFC中线程创建的MFC函数,首先创建了相应的CWinThread对象,然后调用CWinThread:CreateThread, 在CWinThread:CreateThread中,完成了对线程对象的初始化工作,然后,调用_beginthreadex(AfxBeginThread相比较更为安全)创建线程。它简化了操作或让线程能够响应消息,即可用于界面线程,也可以用于工作者线程,但要注意不要在一个MFC程序中使用_beginthreadex()或CreateThread()。,使用准则:,如果线程函数中只调用 Win32 API ,就放心用 CreateThread; 如果要用到C Runtime库,那么就要使用 _beginthreadex ,并且需要在编译环境中选择 Use MultiThread Lib/DLL; MFC程序中使用AfxBeginThread。 坚决不要使用_beginthread(无法创建带有安全属性的新线程,无法创建暂停的线程,也无法获得线程的ID值, ).,2.3 线程函数原型介绍,CreateThread creates a worker thread, 举例 ,_beginthreadex creates a worker thread unsigned long _beginthreadex( void *security, unsigned stack_size, Unsigned ( _stdcall *start_address )( void * ), Void *arglist, Unsigned initflag, Unsigned *thrdaddr );, 举例 ,compile with: /MT ,AfxBeginThread creates a worker thread CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );, 举例 ,AfxBeginThread creates a user-interface thread CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );, 举例 ,2.4 线程深入分析,(1) 线程的创建和初始化,调用CreateThread时,系统创建一个线程内核对象,该对象的初始使用计数是2;线程的内核对象的其他属性也被初始化,暂停计数被设置为1,退出代码始终为STILL_ACTIVE(0x103),该对象设置为 nonsignaled state。,每个线程都有它自己的一组CPU寄存器,称为线程的上下文。 线程的这组CPU寄存器保存在一个CONTEXT结构(在WinNT.h头文件中定义)中。x86类型CPU对应的CONTEXT结构 指令指针(IP)和堆栈指针(SP)寄存器是线程上下文中两个最重要的寄存器。 当线程的内核对象被初始化时, CONTEXT结构的堆栈指针寄存器被设置为线程堆栈上用来放置pfnStartAddr的地址。指令指针寄存器置为称为BaseThreadStart的函数的地址中。该函数包含在Kernel32.dll模块中(这也是实现CreateThread函数的地方).,BaseThreadStart函数执行的基本操作:,该函数实际上是线程开始执行的地方.,(2) C/C+运行期库的考虑,当实现任何类型的编程项目时,必须知道将哪个库与你的项目 相链接,如下图:,为什么将一个库用于单线程应用程序,而将另一个库用于多线程程序? 原因是,标准C运行期库是1970年问世的,它远远早于线程在任何应用程序上的应用。运行期库的发明者没有考虑到将C运行期库用于多线程应用程序的问题。,(3) 线程终止运行 若要终止线程的运行,可以使用下面的方法: 线程函数返回(最好使用这种方法)。 通过调用ExitThread/_endthreadex /AfxEndThread函数,线程将自行撤消(最好不要使用这种方法)。 同一个进程或另一个进程中的线程调用TerminateThread函数(应该避免使用这种方法)。 包含线程的进程终止运行(应该避免使用这种方法)。,线程函数返回(通过设置条件,如信号对象、事件对象等) 始终都应该将线程设计成这样的形式,这是确保所有线程资源被正确地清除的唯一办法。如果线程能够返回,就可以确保下列事项的实现: 在线程函数中创建的所有C + +对象均将通过它们的撤消函数正确地撤消。 操作系统将正确地释放线程堆栈使用的内存。 系统将线程的退出代码(在线程的内核对象中维护)设置为线程函数的返回值。 系统将递减线程内核对象的使用计数。,当线程终止运行时,会发生下列操作: All User object handles owned by the thread are freed. The threads exit code changes from STILL_ACTIVE to the code passed to ExitThread or TerminateThread. The state of the thread kernel object becomes signaled.(这样,可以在主线中利用WaitForSingleObject函数等待线程终止运行. ) If the thread is the last active thread in the process, the system considers the process terminated as well. The thread kernel objects usage count is decremented by 1.,2.5 线程的调度、优先级和亲缘性,使用VC+6.0的Spy+这个工具,可以查看进程中线程的情况:,2.5.1 线程的暂停、恢复、休眠、切换函数 (1)暂停和恢复线程的运行,(2) 睡眠,线程也能告诉系统,它不想在某个时间段内被调度。这是通过调用Sleep函数来实现的:,关于Sleep函数,下面几个重要问题值得注意: 调用Sleep,可使线程自愿放弃它剩余的时间片。 Sleep(n): 系统将在大约的n毫秒数内使线程不可调度,也可能是数秒钟或者数分钟。记住, Windows不是个实时操作系统。虽然线程可能在规定的时间被唤醒,但是它能否做到,取决于系统中还有什么操作正在进行。 Sleep(0): 这将告诉系统,调用线程将释放剩余的时间片,并迫使系统调度另一个线程。但是,系统可以对刚刚调用Sleep的线程重新调度。如果不存在多个拥有相同优先级的可调度线程,就会出现这种情况。,(3)切换到另一个线程,BOOL SwitchToThread(VOID); 调用SwitchToThread函数与调用Sleep(0)相似; 差别是SwitchToThread允许优先级较低的线程运行。即使低优先级线程迫切需要CPU时间,Sleep也能够立即对调用线程重新进行调度。,2.5.2 线程的优先级,每个线程都会被赋予一个从0(最低)到3 1(最高)的优先级号码。 高优先级线程将抢在低优先级线程之前运行,不管低优先级线程正在运行什么。 当设计一个应用程序时,你应该考虑到还有什么别的应用程序会与你的应用程序一道运行。然后,应该根据你的应用程序中的线程应该具备何种响应性,选择一个优先级类。 一旦选定了优先级类之后,就不必考虑你的应用程序与其他应用程序之间的关系,只需要集中考虑你的应用程序中的各个线程的相对优先级。,先确定应用程序优先级类,再确定线程相对优先级,Windows支持6个优先级类:,Windows支持7个相对的线程优先级,进程优先级类和线程相对优先级的映射,注意:如果改变了进程的优先级类,线程的相对优先级不变,但是它的优先级的等级却发生了变化。,2.5.3 优先级编程,进程优先级类的设置方法: (1)当调用CreateProcess时,可以在fdwCreate参数中传递需要的优先级类(下表所示);,(2)调用SetPriorityClass来改变进程自己的优先级类: BOOL SetPriorityClass( HANDLE hProcess, / handle to process DWORD dwPriorityClass / priority class ); 例如: SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); 查看例子 另外: DWORD GetPriorityClass( HANDLE hProcess ) _retrieves the priority class for the specified process.,线程相对优先级的设置方法: 当一个线程刚刚创建时,它的相对线程优先级总是设置为正常优先级。若要设置和获得线程的相对优先级,必须调用下面的函数:,BOOL SetThreadPriority( HANDLE hThread, int nPriority );,int GetThreadPriority( HANDLE hThread );, 举例 ,2.5.4 动态提升线程优先级,进程优先级类和线程相对优先级的映射值称为线程的基本优先级。 系统常常要动态提高线程的优先级,以便对窗口消息或读取磁盘等I/O事件作出响应。,例如,在高优先级类进程中的一个正常优先级等级的线程的基本优先级等级是13。如果用户按下一个操作键,系统就会将一个WM_KEYDOW N消息放入

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论