Windows系统编程---第8章.ppt_第1页
Windows系统编程---第8章.ppt_第2页
Windows系统编程---第8章.ppt_第3页
Windows系统编程---第8章.ppt_第4页
Windows系统编程---第8章.ppt_第5页
已阅读5页,还剩48页未读 继续免费阅读

下载本文档

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

文档简介

Windows系统编程实用教程 授课教师 职务 第8章多线程编程 课程描述在应用程序中使用多线程编程可以提高应用程序的并发性和处理速度 使后台计算不影响前台界面和用户的交互 本章将介绍线程的概念和多线程编程的方法 本章知识点 8 1线程的概念8 2多线程编程基础8 3线程同步 8 1线程的概念 8 1 1什么是线程8 1 2线程内核对象8 1 3线程的状态 8 1 1什么是线程 线程是操作系统可以调度的最小执行单位 通常是将程序拆分成2个或多个并发运行的任务 一个线程就是一段顺序程序 但是线程不能独立运行 只能在程序中运行 不同的操作系统实现进程和线程的方法也不同 但大多数是在进程中包含线程 Windows就是这样 一个进程中可以存在多个线程 线程可以共享进程的资源 比如内存 而不同的进程之间则是不能共享资源的 在单处理器的计算机中 系统会将CPU时间拆分给多线程 处理器在不同的线程之间切换 而在多处理器或多核系统中 线程则是真正地同时运行 每个处理器或内核运行一个线程 线程与进程的对比 进程通常可用独立运行 而线程则是进程的子集 只能在进程运行的基础上运行 进程拥有独立的私有内存空间 一个进程不能访问其他进程的内存空间 而一个进程中的线程则可以共享内存空间 进程之间只能通过系统提供的进程间通信的机制进行通信 而现场间的通信则简单得多 一个进程中的线程之间切换上下文比不同进程之间切换上下文要高效得多 8 1 2线程内核对象 线程内核对象就是一个包含线程状态信息的数据结构 每次成功创建线程后 系统都会在内部为新线程分配一个内核对象 Windows提供的管理线程的函数其实就是通过访问线程内核对象来实现的 内核对象是系统内核分配的一块内存 它只能有运行在内核模式下的程序访问和管理 OpenThread 函数 可以调用OpenThread 函数打开线程内核对象 UsageCount的值会再次加1 函数用法如下 HANDLEOpenThread DWORDdwDesiredAccess 想要的访问权限 可以为THREAD ALL ACCESS等BOOLbInheritHandle 指定此函数返回的句柄是否可以被子进程继承DWORDdwThreadId 目标线程ID号 在结束线程时 需要调用CloseHandle 函数关闭线程 从而会使UsageCount的值减1 SuspendThread 函数 可以调用SuspendThread 函数挂起一个线程的运行 函数原型如下 DWORDWINAPISuspendThread inHANDLEhThread 挂起线程的句柄 8 2阻塞与非阻塞模式Socket编程 8 2 1设置非阻塞模式套接字8 2 2非阻塞模式服务器应用程序编程实例8 2 3非阻塞模式客户端应用程序编程实例8 2 4基于非阻塞模式的多线程服务器应用程序编程实例 ResumeThread 函数 DWORDWINAPIResumeThread inHANDLEhThread 唤醒线程句柄 8 1 3线程的状态 8 2线程编程基础 8 2 1创建线程8 2 2终止线程8 2 3线程的优先级 8 2 1创建线程 可以调用CreateThread 函数创建新线程 函数原型如下 HANDLECreateThread LPSECURITY ATTRIBUTESlpThreadAttributes 指向LPSECURITY ATTRIBUTES结构体的指针 用于决定CreateThread 函数返回的句柄是否可以被子进程继承 如果该参数为NULL 则返回的句柄不能被继承 DWORDdwStackSize 指定线程堆栈的初始大小 单位为字节LPTHREAD START ROUTINElpStartAddress 指定线程函数的起始地址LPVOIDlpParameter 指定传递给线程函数的参数DWORDdwCreationFlags 指定创建线程后是否立即启动 如果指定为CREATE SUSPENDED 则线程创建后处于挂起状态 如果将该参数指定为0 则线程创建后立即运行DWORD lpThreadId 用于取得内核给新生成的线程分配的线程ID号 线程函数的声明格式 DWORDWINAPIThreadProc LPVOIDpParam 函数体 CloseHandle 函数 线程函数执行完成后 可以调用CloseHandle 函数关闭线程句柄 函数原型如下 BOOLCloseHandle HANDLEhObject 要关闭的句柄 例8 1 在主函数中启动10个线程 每个线程中打印在主函数中指定的1 10之间的一个数字 线程函数的名称为ThreadProc 代码如下 线程函数DWORDWINAPIThreadProc LPVOIDlpParam inti int lpParam 获取线程函数的参数printf Iamfromathread num d n i 打印参数return0 主函数的代码 int tmain intargc TCHAR argv 10次创建线程 执行ThreadProc 函数for inti 1 i 10 i HANDLEhThread 线程句柄DWORDdwThreadId 线程ID 创建线程CreateThread NULL 指定线程句柄不可继承NULL 采用默认的线程堆栈大小ThreadProc 指定线程函数 LPVOID i 指定线程函数使用的参数 即在ThreadProc 函数中指定变量i的值0 指定线程立即运行 例8 1 的运行结果 可以看到 窗口中打印了10个线程的输出结果 尽管在创建线程时按照1 10的顺序来传递参数 但在线程中输出的顺序却被打乱了 这说明线程几乎是同时被执行的 并不是执行完一个线程然后再创建另外一个线程 尝试多次执行程序 可以看到每次输出的顺序都是不同的 8 2 2终止线程 当线程被正常终止时 系统会进行下面的操作 在线程函数中创建的所有C 对象将通过它们各自的析构函数被正确地销毁 该线程使用的堆栈将被释放 系统将线程内核对象中ExitCode 退出代码 的值由STILL ACTIVE设置为线程函数的返回值 系统将递减线程内核对象中UsageCode的值 1 线程函数的自然退出 当函数执行到return语句返回时 Windows将终止线程的执行 一般建议使用这种方法终止一个线程的执行 2 使用ExitThread 函数来终止线程 voidExitThread DWORDdwExitCode 线程的退出代码 3 使用TerminateThread 函数 BOOLTerminateThread HANDLEhThread 目标线程句柄DWORDdwExitCode 目标线程的退出代码 4 使用ExitProcess 函数结束进程 此时系统会自动结束进程中所有线程的运行 用这种方法相当于对每个线程使用TerminateThread 函数 因此也应当避免这种情况 8 2 3线程的优先级 每个线程都有一个优先级号 取值为0 最低 31 最高 Windows支持6个优先级类 idle belownormal normal abovenormal high和real time 顾名思义 normal是被绝大多数应用程序采用的优先级类 线程刚被创建时 它的相对优先级被设置为normal 可以调用SetThreadPriority 函数来设置线程的优先级 函数原型如下 BOOLSetThreadPriority HANDLEhThread intnPriority 8 3线程同步 8 3 1什么是线程同步8 3 2等待函数8 3 3临界区对象8 3 4事件内核对象 8 3 1什么是线程同步 例8 2 下面演示一个有问题的程序 该程序用两个线程来同时增加全局变量g nCount1和g nCount2的计数 代码如下 include stdafx h include includeintg nCount1 0 intg nCount2 0 BOOLg bContinue TRUE UINT stdcallThreadFunc LPVOID int tmain intargc TCHAR argv UINTuId HANDLEh 2 接上 h 0 HANDLE beginthreadex NULL 0 ThreadFunc NULL 0 接上 UINT stdcallThreadFunc LPVOID while g bContinue g nCount1 g nCount2 return0 例8 2 的运行结果 8 3 2等待函数 1 单对象等待函数2 多对象等待函数 1 单对象等待函数 调用WaitForSingleObject 函数在指定对象处于受信 signaled 状态或超过超时时间时返回 函数原型如下 DWORDWaitForSingleObject HANDLEhHandle 指定对象的句柄 通常可以使用线程句柄 当线程结束时 线程句柄变成受信状态DWORDdwMilliseconds 超时时间 单位为毫秒 如果为INFINITE 则不设置超时时间 如果函数执行成功 则返回代表导致函数返回的事件的数值 例8 3 演示WaitForSingleObject 函数的使用 代码如下 include stdafx h include 线程函数voidThreadMain LONGPassVal LONGi for i 0 i PassVal i printf ThreadMain count d n i int tmain intargc TCHAR argv DWORDThreadID dwRet HANDLEhTh 创建线程 执行ThreadMain 函数hTh CreateThread NULL 0 LPTHREAD START ROUTINE ThreadMain LPVOID 10 0 接上 if hTh NULL printf CreateThread failed error u n GetLastError system pause return1 elseprintf CreateThread isOK threadID u n ThreadID 等待子线程printf 等待子线程 n dwRet WaitForSingleObject hTh INFINITE printf WaitForSingleObject 函数的返回值为X 8X n dwRet switch dwRet caseWAIT ABANDONED printf 互斥对象没有被线程释放 n break 接上 caseWAIT OBJECT 0 printf 调用线程的状态变为受信 n break caseWAIT TIMEOUT printf 超时时间已过 n break caseWAIT FAILED printf WaitForSingleObject 函数失败 错误代码 u n GetLastError ExitProcess 0 if CloseHandle hTh 0 printf 子线程句柄hTh成功被关闭 n elseprintf 关闭子线程句柄hTh失败 错误代码 u n GetLastError printf 主进程和线程即将退出t n n system pause return0 例8 3 的运行结果 例8 4 去掉 例8 3 中与WaitForSingleObject 函数相关的代码 从而演示WaitForSingleObject 函数的作用 2 多对象等待函数 DWORDWINAPIWaitForMultipleObjects inDWORDnCount 数组lpHandles中对象的数量 inconstHANDLE lpHandles 对象句柄数组 inBOOLbWaitAll 是否数组lpHandles中所有对象都变为收信状态 函数才返回 inDWORDdwMilliseconds 超时时间 单位为毫秒 如果为INFINITE 则不设置超时时间 例8 5 演示WaitForMultipleObjects 函数函数的使用 程序首先3次调用CreateThread 函数创建线程执行ThreadMain 函数 线程句柄保存在数组hTh 中 在主进程中以hTh为参数调用WaitForMultipleObjects 函数等待线程结束 当所有线程的ThreadMain 函数执行完成后 WaitForMultipleObjects 函数返回 程序的运行结果如图8 8所示 例8 6 去掉 例8 5 中与WaitForMultipleObjects 函数相关的代码 从而演示WaitForMultipleObjects 函数的作用 8 3 3临界区对象 当多个线程在同一个进程中执行时 可能有不止一个线程同时执行同一段代码 访问同一段内存中的数据 当多个线程同时读和写共享数据时 则可能出现错误 临界区对象是定义在数据段中的一个CRITICAL SECTION结构 Windows内部使用这个结构记录一些信息 确保在同一时间只有一个线程访问该数据段中的数据 InitializeCriticalSection 函数 voidInitializeCriticalSection LPCRITICAL SECTIONlpCriticalSection 指向数据段中定义的CRITICAL SECTION结构 EnterCriticalSection 函数 voidEnterCriticalSection LPCRITICAL SECTIONlpCriticalSection LeaveCriticalSection 函数 当操作完成的时候 还要将临界区交还给Windows 以便其他线程可以申请使用 这个工作由LeaveCriticalSection 函数来完成 voidLeaveCriticalSection LPCRITICAL SECTIONlpCriticalSection DeleteCriticalSection 函数 当程序不再使用临界区的时候 必须使用DeleteCriticalSection 函数将它删除 函数原型如下 voidDeleteCriticalSection LPCRITICAL SECTIONlpCriticalSection 例8 7 使用临界区对象来改写例8 2中存在同步问题的计数程序 8 3 4事件内核对象 事件对象是一种抽象的对象 它也包括未受信 nonsignaled 和受信 signaled 两种状态 可以把事件对象看成是一个设置在Windows内部的标志 它的状态设置和测试工作由Windows来完成 CreateEvent 的函数 HANDLECreateEvent LPSECURITY ATTRIBUTESlpEventAttributes 用来定义事件对象的安全属性BOOLbManualReset 指定是否需要手动重置事件对象为未受信状态 BOOLbInitialState 指定事件对象创建时的初始状态LPCWSTRlpName 事件对象的名称 OpenEvent 函数 OpenEvent 函数用于打开事件对象 函数原型如下 HANDLEOpenEvent DWORDdwDesiredAccess 指定想要的访问权限BOOLbInheritHandle 指定返回句柄

温馨提示

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

评论

0/150

提交评论