Windows多线程编程.ppt_第1页
Windows多线程编程.ppt_第2页
Windows多线程编程.ppt_第3页
Windows多线程编程.ppt_第4页
Windows多线程编程.ppt_第5页
已阅读5页,还剩106页未读 继续免费阅读

下载本文档

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

文档简介

第二章Windows多线程编程 内容 Windows操作系统的一些基本知识Win32API线程库线程间通信 1 Windows操作系统的一些基本知识 API内核对象及句柄 API ApplicationProgrammingInterface API 操作系统留给应用程序的一个调用接口 应用程序通过API使操作系统去执行相应程序 WindowsAPI是一套用来控制Windows的各个部件的外观和行为的预先定义的Windows函数 Win32API即为Microsoft32位平台的应用程序编程接口 Win32平台上运行的应用程序都可以调用这些函数 32与64位API函数声明上没有明显区别 但64位编程是需要下载相应的软件开发包SDK API ApplicationProgrammingInterface 直接用win32API编写的应用程序 程序的执行代码小 运行效率高MFC用类库的方式将win32API进行封装 以类的方式提供给开发者 内核对象及句柄 内核对象是由操作系统内核分配 只能由内核访问的数据结构 供系统和应用程序使用来管理各种系统资源 内核对象包括 进程对象 线程对象 事件对象 文件对象 作业对象 互斥对象 等待计时器对象等都是内核对象 出于安全的考虑 进程不能直接访问内核对象 操作系统提供了一组函数来访问内核对象 通过函数创建 打开和操作内核对象 内核对象及句柄 内核对象由内核拥有 各个进程可以共享内核对象 进程终止执行 它使用的内核对象并不一定会被撤销 每个内核对象都有一个计数器来存储有多少个进程在使用它的信息 进程调用时 计数器增1 调用结束 计数器减1 内核对象计数器为零时 销毁此内核对象 内核对象及句柄 内核对象有安全描述符的保护 安全描述符描述了谁创建了该对象以及谁能够使用该对象 用于创建内核对象的函数几乎都有一个指向SECURITY ATTRIBUTES结构的指针作为其参数 大多数应用程序通过传NULL值 创建具有默认安全性的对象 如果想限制其他线程对对象的访问 就需要单独创建一个SECURITY ATTRIBUTES对象并对其初始化 内核对象及句柄 句柄 创建内核对象时 函数的返回值 标记该内核对象 句柄表 进程被初始化时 系统给进程分配一个句柄表 用于保存该进程使用的内核对象的信息 而句柄值则是相应内核对象在句柄表中的索引值 因此句柄值是进程相关的 内核对象及句柄 内核对象创建当利用creat 函数来创建内核对象时 系统内核就为该对象分配一个内存块 并进行初始化 然后系统内核扫描该进程的句柄表 初始化一条记录并放在句柄表中 关闭内核对象无论进程怎样创建内核对象 在不使用该对象的时候都应当通过BoolCloseHandle HANDLEhobj 来向操作统声明结束对该对象的访问 Win32API线程库 创建线程的基本问题创建线程的API函数操作线程的API一个简单的Windows多线程程序 1创建线程的基本问题 线程可以由进程中的任意线程创建 而进程的主线程在进程加载时自动创建 每个线程都有自己的进入点函数 主线程的进入点函数 线程函数的返回值是该线程的退出代码线程函数应尽可能使用函数参数和局部变量 线程函数 线程的入口点 DWORDWINAPIThreadFunc PVOIDpvParam DWORDdwResult 0 return dwResult 2创建线程的API函数 创建线程过程 系统创建一个线程内核对象 线程内核对象不是线程本身 而是操作系统用来管理线程的较小的数据结构 在进程的地址空间分配内存 供线程的堆栈使用 HANDLECreateThread PSECURITY ATTRIBUTESpsa DWORDcbStack PTHREAD START ROUTINEpStartAddr PVOIDpvParam DWORDfdwCreate PDWORDpdwThreadId 2创建线程的API函数 NULL 0 函数地址 函数参数NULL 控制创建线程标志CREATE SUSPENDED0 线程ID include windows h includeusingnamespacestd DWORDWINAPIThreadFunc PVOIDpvParam cout Createdthreadsays helloWorld endl return0 intmain HANDLEThreadHandle CreateThread NULL 0 ThreadFunc NULL 0 NULL Sleep 100 cout Mainthreadsays HelloWorld endl getchar return0 暂停线程DWORDSuspendThread HANDLEhThread 返回值是线程的前一个暂停计数暂停计数 是线程内核对象的一个内部值 使用要小心 因为不知道暂停线程运行时它在进行什么操作 可能造成死锁 3操作线程的API 3操作线程的API 恢复线程DWORDResumeThread HANDLEhThread 返回值是线程的前一个暂停计数该函数用于将处于暂停状态的线程置于就绪状态 使其参加线程调度 3操作线程的API 使线程睡眠VOIDSleep DWORDdwMilliseconds 该函数是线程暂停自己的运行 直到睡眠时间过去为止 当线程调用这个函数时 它自动放弃剩余的时间片 迫使系统进行线程调度 Windows不是实时的操作系统 3操作线程的API 终止线程线程函数返回 最好 通过调用ExitThread函数 线程将自行撤销同一个进程或另一个进程中的线程调用TerminateThread函数包含线程的进程终止 线程返回函数 线程中创建的C 类对象能够正常撤销 操作系统将正确地释放线程堆栈使用的内存 系统将线程的退出代码设置为线程函数的返回值 系统将递减线程内核对象的使用计数 线程调用这个函数 强制线程终止运行 操作系统清除该线程使用的所有系统资源 C 类对象将不被撤销 VOIDExitThread DWORDdwExitCode ExitThread函数 能够撤销任何线程 线程的内核对象的使用计数也被递减 异步运行的函数 不撤销线程的堆栈 直到进程终止 BOOLTerminateThread HANDLEhThread DWORDdwExitCode TerminateThread函数 在进程终止运行时撤销线程 ExitProcess和TerminateProcess函数可以终止线程 将会终止进程中的所有线程 ExitProcess只能强制执行本进程的退出 TerminateProcess在一个进程中强制结束其他的进程 进程所使用的资源被清除 C 对象撤销函数没有被调用 VOIDExitProcess UINTuExitCode BOOLTerminateProcess HANDLEhProcess UINTuExitCode include includeusingnamespacestd DWORDWINAPIFunOne LPVOIDparam int p int param cout p endl while true Sleep 1000 cout hello return0 DWORDWINAPIFunTwo LPVOIDparam int p int param cout p endl while true Sleep 1000 cout world return0 intmain intargc TCHAR argv intinput 100 intinput1 100 intinput2 200 HANDLEhand1 CreateThread NULL 0 FunOne void while true cin input if input 1 ResumeThread hand1 ResumeThread hand2 else SuspendThread hand1 SuspendThread hand2 TerminateThread hand1 1 TerminateThread hand2 1 return0 例 打印出100 1000之间的所有 水仙花数 所谓 水仙花数 是指一个三位数 其各位数字立方和等于该数本身 例如 153是一个 水仙花数 因为153 13 33 53不需要通信 intmain inti j k n printf 水仙花数 是 for n 100 n 1000 n i n 100 j n 10 i 10 k n 10 if i 100 j 10 k i i i j j j k k k printf d n printf n getchar return0 structbound intlow inthigh DWORDWINAPIThread1 PVOIDpvParam inti j k n bound bou bound pvParam intlow bou low inthigh bou high for n low n high n i n 100 j n 10 i 10 k n 10 if i 100 j 10 k i i i j j j k k k printf d n return0 intmain printf 水仙花数 是 boundqw1 qw2 qw1 low 100 qw1 high 500 HANDLEThreadHandle1 CreateThread NULL 0 Thread1 线程间通信 操作系统随机调度线程 程序员不能预知线程的执行顺序下面两种情况下 线程间需要通信当有多个线程访问共享资源而不希望共享资源遭到破坏 互斥 当一个线程需要将某个任务已经完成的情况通知另外一个或多个线程时 同步 Windows线程通信方法主要有互锁函数 临界段 事件 互斥量 信号量 线程间通信 互锁函数临界段事件互斥量信号量 使用内核对象的线程间通信 互锁函数和临界段都是在用户态实现线程通信的 优点速度快用户态机制只能实现同一进程内线程通信 内核对象机制可以实现不同进程内线程的通信 缺点速度慢 包含通知状态和未通知状态内核属性的内核对象有 进程 线程 作业 文件 控制台输入文件修改通知 事件 可等待定时器信号量 互斥量 等待函数 使线程进入等待状态 直到一个对象变为已通知状态 DWORDWaitForSingleObject HANDLEhHandle DWORDdwMilliseconds 参数dwMilliseconds有两个特殊值 0 则该函数立即返回 INFINITE 则线程被挂起 直到hHandle所指向的对象变为已通知状态 使用内核对象的线程间通信 等待函数 可以保证线程的同步 DWORDWaitForMultipleObject DWORDdwCount CONSTHANDLE phHandle BOOLfWaitAll DWORDdwMilliseconds 使用内核对象的线程间通信 1 互锁函数 互锁函数是用来解决原子访问的 主要针对变量的原子访问 原子访问 当线程访问资源时 能够确保没有其它线程同时访问相同的资源 Longg x 0 全局变量DWORDWINAPIThreadFunc1 PVOIDpvParam g x return0 DWORDWINAPIThreadFunc2 PVOIDpvParam g x return0 MOVEAX g x INCEAXMOV g x EAX 递增以原子方式运行 1 互锁函数 例 1 互锁函数 LONGInterlockedExchangeAdd PLONGplAddend LONGlIncrement Longg x 0 全局变量DWORDWINAPIThreadFunc1 PVOIDpvParam InterlockedExchangeAdd 1 互锁函数 以原子操作方式用第二个参数的值取代第一个参数的当前值 LONGInterlockedExchange PLONGplTarget LONGlValue LONGInterlockedExchangePointer PVOID ppvTarget PVOIDpvValue 1 互锁函数 比较第一个参数所指的值和第三个参数的值 如果相等 则将第一个参数所指的值置为第二个参数 如果不相等则不进行任何操作 LONGInterlockedCompareExchange PLONGplDestination LONGlExchange LONGlComparand LONGInterlockedCompareExchangePointer PVOID ppvDestination PVOIDpvExchange PVOIDpvComparand 例 10000个2相加 intmain intsum 0 for inti 1 i 10000 i sum sum 2 printf 10000个2相加之和是 d sum getchar return0 include windows h longsum 0 DWORDWINAPIThread1 PVOIDpvParam for inti 1 i 5000 i InterlockedExchangeAdd DWORDWINAPIThread2 PVOIDpvParam for inti 5001 i 10000 i InterlockedExchangeAdd intmain HANDLEThreadHandle1 CreateThread NULL 0 Thread1 NULL 0 NULL HANDLEThreadHandle2 CreateThread NULL 0 Thread2 NULL 0 NULL HANDLEThreadHandles 2 ThreadHandle1 ThreadHandle2 WaitForMultipleObjects 2 ThreadHandles TRUE INFINITE printf 10000个2相加之和是 d sum getchar return0 2 临界段 互锁函数 以原子操作方式修改单个值临界段 以原子方式修改复杂的数据结构 临界段 关键代码段 是指一小段代码 同一个时刻 只能有一个线程具有访问权 多个线程访问同一个临界区的原则 一次最多只能一个线程停留在临界区内 不能让一个线程无限地停留在临界区内 否则其它线程将不能进入该临界区 2 临界段相关API函数 首先定义一个临界段对象 通常全局变量 CRITICAL SECTIONcs临界段对象初始化InitializeCriticalSection cs 进入临界段EnterCriticalSection cs 离开临界段LeaveCriticalSection cs 释放临界段对象DeleteCriticalSection cs 临界段例1 include includefstreamfile DWORDWINAPIThreadFunc1 PVOIDparam for inti 1 i 1000 i file ThreadFunc1Output i endl return0 DWORDWINAPIThreadFunc2 PVOIDparam for inti 1 i 1000 i file ThreadFunc2Output i endl return0 intmain file open data txt ios out HANDLEThreadHandle1 CreateThread NULL 0 ThreadFunc1 NULL 0 NULL HANDLEThreadHandle2 CreateThread NULL 0 ThreadFunc2 NULL 0 NULL HANDLEhThread 2 ThreadHandle1 ThreadHandle2 WaitForMultipleObjects 2 hThread TRUE INFINITE file close return0 加上临界段 include includefstreamfile CRITICAL SECTIONcs DWORDWINAPIThreadFunc1 PVOIDparam for inti 1 i 1000 i EnterCriticalSection DWORDWINAPIThreadFunc2 PVOIDparam for inti 1 i 1000 i EnterCriticalSection include windows h 例2CRITICAL SECTIONg cs charg cArray 10 共享资源DWORDWINAPIThreadProc1 PVOIDpParam EnterCriticalSection DWORDWINAPIThreadProc2 PVOIDpParam 进入临界区EnterCriticalSection intmain InitializeCriticalSection 3 事件 事件内核对象是最简单的对象 一个使用计数一个布尔值 指明该事件是自动复位事件 false 还是人工复位事件 true 一个布尔值 指明该事件是已通知状态 true 还是未通知状态 false 当人工复位事件得到通知时 等待该事件的所有线程均变为可调度事件 当自动复位事件得到通知时 等待该事件的线程中只有一个线程变为可调度线程 创建事件内核对象 返回句柄 HANDLECreateEvent PSECURITY ATTRIBUTESpsa 安全属性BOOLfManualReset 复位方式BOOLfInitialState 初始状态PCTSTRpszName 对象名称 3 事件 打开一个已经存在的命名事件对象HANDLEOpenEvent DWORDfdwAccess BOOLfInherit PCTSTRpszName 3 事件 EVENT ALL ACCESS要求对事件对象进行完全访问EVENT MODIFY STATE允许SetEvent和ResetEvent函数SYNCHRONIZE允许事件对象的使用同步 一旦事件已经创建 就可以直接控制它的状态将事件设置为已通知状态BOOLSetEvent HANDLEhEvent 将事件设置为未通知状态BOOLResetEvent HANDLEhEvent 3 事件 3 事件 事件的主要用途是标志事件的发生 并以此协调线程的执行顺序 例1 用户在主线程输入命令 控制新建线程的运行 include include includeusingnamespacestd CRITICAL SECTIONcs DWORDWINAPIThreadFunc PVOIDparam EnterCriticalSection 例1 intmain InitializeCriticalSection cin input if input continue cout MainThread Letthreadcontinuerun endl SetEvent hEvent break WaitForSingleObject ThreadHandle INFINITE cout MainThread Createthreadfinished endl DeleteCriticalSection 例2 设计简单的文字管理软件 要求实现功能 读文件 字数统计 拼写检查 语法检查 includeHANDLEg hEvent voidOpenFileAndReadContentsIntoMemory printf OpenFileandReadcontentsintomemory n DWORDWINAPIWordCount PVOIDpvParam WaitForSingleObject g hEvent INFINITE printf 0 wordcount n SetEvent g hEvent 自动return 0 DWORDWINAPISpellCheck PVOIDpvParam WaitForSingleObject g hEvent INFINITE printf 1 Spellcheck n Accessthememoryblock SetEvent g hEvent 自动return 0 DWORDWINAPIGrammarCheck PVOIDpvParam WaitForSingleObject g hEvent INFINITE printf 2 Grammarcheck n Accessthememoryblock SetEvent g hEvent 自动return 0 intmain g hEvent CreateEvent NULL TRUE FALSE NULL 人工重置g hEvent CreateEvent NULL FALSE FALSE NULL 自动重置HANDLEhThread 3 DWORDdwThreadID 3 hThread 0 CreateThread NULL 0 WordCount NULL 0 OpenFileAndReadContentsIntoMemory Allowall3threadstoaccessthememory SetEvent g hEvent WaitForMultipleObjects 3 hThread TRUE INFINITE printf mainthreadexit n getchar return0 例3 协调两个线程执行顺序 读操作 写操作先写后读 HANDLEevRead evFinish DWORDWINAPIReadThread PVOIDparam WaitForSingleObject evRead INFINITE cout Reading endl SetEvent evFinish DWORDWINAPIWriteThread PVOIDparam cout Writing endl SetEvent evRead intmain evRead CreateEvent NULL FALSE FALSE NULL evFinish CreateEvent NULL FALSE FALSE NULL CreateThread NULL 0 ReadThread NULL 0 NULL CreateThread NULL 0 WriteThread NULL 0 NULL WaitForSingleObject evFinish INFINITE cout TheProgramisEnd endl return0 4 互斥量 互斥量是一个种内核对象 确保线程拥有对单个资源的互斥访问权 一个使用数量一个线程ID一个递归计数器互斥量的线程ID标识系统中哪个线程拥有互斥量 为0 没有线程拥有递归计数器指明线程拥有互斥量的次数 4 互斥量 经常用于保护多个线程访问的内存块 控制对共享资源的访问保证每次只能有一个线程获得互斥量 4 互斥量 互斥量的创建 返回句柄HANDLECreateMutex PSECURITY ATTRIBUTESpsa 安全属性的指针BOOLbInitialOwner 初始化互斥对象的所有者PCTSTRpszName 指向互斥对象名的指针 InitialOwner FALSE 互斥对象的线程ID和递归计数器均被设置为0 TRUE 互斥对象的线程ID被设置为调用线程的ID 递归计数器被设置为1 4 互斥量 为现有的一个已命名互斥对象创建一个新句柄HANDLEOpenMutex DWORDfdwAccess accessBOOLbInheritHandle inheritanceoptionPCTSTRpszName objectname MUTEX ALL ACCESS请求对互斥对象的完全访问MUTEX MODIFY STATE允许使用ReleaseMutex函数SYNCHRONIZE允许使用互斥对象同步 释放互斥量HANDLEReleaseMutex HANDLEhMutex 等待互斥量DWORDWaitForSingleObject HANDLEhHandle DWORDdwMilliseconds 互斥量不同于其它内核对象 互斥对象有一个 线程所有权 的概念 4 互斥量 include includeusingnamespacestd fstreamfile DWORDWINAPIThreadFunc1 PVOIDparam HANDLE phMutex HANDLE param for inti 1 i 100 i WaitForSingleObject phMutex INFINITE file ThreadFunc1Output i endl ReleaseMutex phMutex return0 例1 DWORDWINAPIThreadFunc2 PVOIDparam HANDLE phMutex HANDLE param for inti 1 i 100 i WaitForSingleObject phMutex INFINITE file ThreadFunc2Output i endl ReleaseMutex phMutex return0 intmain file open data txt ios out HANDLEhMutex CreateMutex NULL FALSE DisplayMutex HANDLEThreadHandle1 CreateThread NULL 0 ThreadFunc1 比较临界区 互斥量 事件 给数组元素赋值 并在屏幕打印出来改变起始值 无限重复上述过程 include windows h CRITICAL SECTIONcs inta 5 DWORDWINAPIThread PVOIDpParams inti num 0 while TRUE EnterCriticalSection 临界区 intmain InitializeCriticalSection 临界区 includeHANDLEhMutex inta 5 DWORDWINAPIThread PVOIDpParams inti num 0 while TRUE WaitForSingleObject hMutex INFINITE for i 0 i 5 i a i num ReleaseMutex hMutex num return0 互斥量 intmain hMutex CreateMutex NULL FALSE NULL CreateThread NULL 0 Thread NULL 0 NULL while TRUE WaitForSingleObject hMutex INFINITE printf d d d d d n a 0 a 1 a 2 a 3 a 4 ReleaseMutex hMutex return0 互斥量 includeHANDLEhEvent1 hEvent2 inta 5 DWORDWINAPIThread PVOIDpParams inti num 0 while TRUE WaitForSingleObject hEvent2 INFINITE for i 0 i 5 i a i num SetEvent hEvent1 num return0 事件 intmain hEvent1 CreateEvent NULL FALSE TRUE NULL hEvent2 CreateEvent NULL FALSE FALSE NULL CreateThread NULL 0 Thread NULL 0 NULL while TRUE WaitForSingleObject hEvent1 INFINITE printf d d d d d n a 0 a 1 a 2 a 3 a 4 SetEvent hEvent2 return0 事件 5 信号量 信号量是一个内核对象 可用来管理大量有限的系统资源一个使用计数32位整数 最大资源数量32位整数 当前资源数量信号量使用规则 当前资源数量大于0 则等待信号量的线程获得资源继续运行 当前资源数量减1当前资源数量等于0 则等待信号量的线程继续等待 直到有线程释放信号量 使当前资源数量大于0 创建信号量 5 信号量 HANDLECreateSemaphore PSECURITY ATTRIBUTESpsa LONGlInitialCount initialcountLONGlMaximumCount maximumcountPCTSTRpszName objectname 为现有的一个已命名信号机对象创建一个新句柄 5 信号量 HANDLEOpenSemaphore DWORDfdwAccess BOOLbInheritHandle inheritanceoptionPCTSTRpszName objectname SEMAPHORE ALL ACCESS要求对信号量的完全访问 SEMAPHORE MODIFY STATE允许使用ReleaseSemaphore函数 SYNCHRONIZE允许使用信号量同步 释放信号量 ReleaseSemaphore HANDLEhSem LONGlReleaseCount PLONGplPreviousCount 5 信号量 等待互斥量DWORDWaitForSingleObject HANDLEhHandle DWORDdwMilliseconds 例 两个线程分别有一个初值为0的int局部变量 两个线程的行为是在一个循环中 使整型变量递增一个约束条件 在递增过程中 这两个值的差不超过5 HANDLEhsem1 CreateSemaphore NULL 5 10 sem1 HANDLEhsem2 CreateSemaphore NULL 5 10 sem2 inti1 0 inti2 0 DWORDWINAPIThreadFunc1 PVOIDparam for inti 1 i 100 i WaitForSingleObject hsem1 INFINITE ReleaseSemaphore hsem2 1 NULL i1 file i1 i1 i2 i2 endl return0 DWORDWINAPIThreadFunc2 PVOIDparam for inti 1 i 100 i WaitForSingleObject hsem2 INFINITE ReleaseSemaphore hsem1 1 NULL i2 file i1 i1 i2 i2 endl return0 生产者 消费者 有一个生产者进程 有两个消费者进程 生产者产生1 100的100个数 两个消费者从共享内存中取数 includeintarray 5 作为仓库存放数据 最多可以放五个数据intpointer 记录生成数据的个数intpointerget 记录取得的数据的位置intsum 用来保存数据和CRITICAL SECTIONcsArray 临界区对象HANDLEhFull 句柄 保存Full信号量HANDLEhEmpty 句柄 保存Empty信号量 生产者函数DWORDWINAPIProducer PVOIDpParam inti 0 pointer 0 while i 100 WaitForSingleObject hEmpty INFINITE EnterCriticalSection 消费者函数ADWORDWINAPIConsumerA LPVOIDlpParam while 1 WaitForSingleObject hFull INFINITE EnterCriticalSection 消费者函数BDWORDWINAPIConsumerB LPVOIDlpParam while 1 WaitForSingleObject hFull INFINITE EnterCriticalSection voidmain HANDLEhThreadProducer hThreadConsumerA hThreadComsumerB sum 0 pointerget 0 InitializeCriticalSection 哲学家吃饭问题 设有5个哲学家 共享一张放有5把椅子的桌子 每人分得一把椅子 但是 桌子上共有5只筷子

温馨提示

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

评论

0/150

提交评论