第18讲windows线程同步技术内核模式_第1页
第18讲windows线程同步技术内核模式_第2页
第18讲windows线程同步技术内核模式_第3页
第18讲windows线程同步技术内核模式_第4页
第18讲windows线程同步技术内核模式_第5页
已阅读5页,还剩36页未读 继续免费阅读

付费下载

下载本文档

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

文档简介

中科研CASoft软件工程师培训讲义Game

Master游戏修改工具第18讲Windows线程同步技术:内核模式的同步中科天地软件人才培训中心Created

by本讲重点提要¤

本讲对使用内核对象的线程同步技术做了较为详细的介绍,包括:等待函数的使用互斥量对象的使用事件对象的使用可等的计时器对象的使用关于信号量的使用,我们放在下一讲进行介绍。内核模式线程同步¤

用户方式同步的优点是速度非常快。如果强调线程的运行速度,那么首先应该确定用户方式的线程同步机制是否适合需要。¤

虽然用户方式的线程同步机制具有速度快的优点,但是它也有其局限性。对于许多应用程序来说,这种机制是不适用的。例如,互锁函数家族只能在单值上运行,根本无法使线程进入等待状态。可以使用关键代码段使线程进入等待状态,但是只能用这些代码段对单个进程中的线程实施同步。还有,使用关键代码段时,很容易陷入死锁状态,因为在等待进入关键代码段时无法设定超时值。内核模式线程同步¤

内核对象机制的适应性远远优于用户方式机制。实际上,内核对象机制的唯一不足之处是它的速度比较慢。当调用本章中提到的任何新函数时,调用线程必须从用户方式转为内核方式。¤

这种线程执行环境的用户模式和内核模式之间的切换对用户透明,但对于系统系统来讲是不小的开销,因此频繁的进行用户模式和内核模式切换将影响应用程序的性能。内核模式线程同步¤

对于线程同步来说,这些内核对象中的每种对象都可以说是处于有信号(Signaled)或无信号(Nonsignaled)的状态之中。这种状态的切换是由Microsoft为每个对象建立的一套规则来决定的。例如,进程和线程内核对象总是在无信号状态中创建的。当进程或线程终止运行时,操作系统自动使该进程或线程的内核对象处于有信号状态。应用程序可以利用进程或线程内核对象的信号状态对其进行等待,从而判断一个进程或线程是否结束。内核模式线程同步¤

线程同步的基本情况为:一个线程继续执行之前希望某个或几个条件满足,或者某个或几个事件发生,线程使自己进入等待状态以等待事件的发生或条件的满足,当其它线程完成相应的任务时,通过某种机制(比如这里的内核同步对象)来通知等待线程,等待线程获得通知后得以继续执行。如果在预定的等待时间内事件没有发生,线程可以返回超时错误。内核模式线程同步¤

下面的示意图显示了线程同步的基本情况。执行线程进入等待继续执行是事件发生?否超时任务线程完成任务发出通知线程同步:等待函数¤

要使线程进入等待状态(或者叫做睡眠或阻塞

状态),必须使用等待函数。等待函数使调用

线程阻塞预定的一段时间,在该时间内如果所

等待的事件发生(变为有信号),则等待满足,线程继续执行,否则,等待超时。¤

线程主要使用下面两个函数使自己进入等待状态:DWORD

WaitForSingleObject

(HANDLE

hObject,

DWORD

dwTimeout

);DWORD

WaitForMultipleObjects

(DWORD

nNumObj,

LPHANDLE

lpHandles,BOOL

fWaitAll, DWORD

dwTimeout

);线程同步:等待函数¤

函数WaitForSingleObject告诉系统等待由参数

hObject标识的内核对象变为有信号。参数

dwTimeout告诉系统线程愿意等待的毫秒数,如果指定的内核对象在指定的时间内变为有信号,线程就会被唤醒继续执行。¤

函数WaitForSingleObject的返回值可能为:WAIT_OBJECT_0:即等待满足。WAIT_TIMEOUT:WAIT_ABANDONED:定义为0值,表示等待对象有信号,定义为0x102,表示超时。定义为0x80,当对象是一个互斥对象时,由于对象被放弃而有信号。关于互斥对象,稍后将会介绍。–

WAIT_FAILED:

定义为0xFFFFFFFF,表示发生了错误。线程同步:等待函数¤

WaitForSingleObject函数的dwTimeout参数可以接受两个特殊的值:0值用于检查某个内核对象的状态。传递0告诉系统线程不想等待,只是要系统回答该对象当前是否有信号。返回值

WAIT_OBJECT_0说明有信号,

WAIT_TIMEOUT说明对象无信号。传递INFINITE(定义为0xFFFFFFFF)告诉系统线程在对象有信号之前将一直等待下去。这种情况下,如果对象永远不变为有信号,线程将被死锁。线程同步:等待函数¤

可以使用类似如下的代码使用该函数:DWORD

dwStatus

=

WaitForSingleObject(hObj,

1000);switch(dwStatus)

{case

WAIT_OBJECT_0://等待满足break;case

WAIT_TIMEOUT: //等待超时break;case

WAIT_FAILED:

//出错break;}线程同步:等待函数¤

函数WaitForMultipleObjects类似于

WaitForSingleObject,只是它要么等待若干个对象都变为有信号,要么等待若干个对象中的一个变为有信号。¤该函数的参数nNumObj指出等待对象的数目,该值不得大于MAXIMUM_WAIT_OBJECTS(定义为64)。¤参数lpHandles是标识这些对象的数组的指针。注意,如果同一个对象在该数组中出现多于一次,即使使用不同的句柄值,也会发生错误。¤参数fWaitAll指出是要等待列表中所有对象还是只要有一个对象有信号就满足等待。线程同步:等待函数¤

WaitForMultipleObjects函数返回值:WAIT_TIMEOUT:

定义为0x102,表示超时。WAIT_FAILED:

定义为0xFFFFFFFF,表示发生了错误。WAIT_OBJECT_0

WAIT_OBJECT_0+nNumObj-1:

从0到nNumObj-1的索引值,表示对象句柄数组中变为有信号的对象的索引,多个对象有信号则返回最小的索引值。当fWaitAll为真时,返回0值,即WAIT_OBJECT_0

。WAIT_ABANDONED_0

WAIT_

ABANDONED

_0

+nNumObj-1:

从0x80开始的索引值,当fWaitAll为真时,表示等待成功,并且其中至少有一个互斥对象的有信号是由于被废弃造成的。当等待任一对象时,表示该索引值的互斥对象由于被废弃而变为有信号。线程同步:等待函数¤

对于后面将要介绍的互斥量、自动重置事件和自动重置可等的计时器对象,当等待满足,

WaitXxx函数将把它们的状态重置为无信号,正因为此,多个线程对一个对象等待时,每次只会有一个线程醒来,其它线程将继续睡眠。¤

对于WaitForMultipleObjects还有一点需要了

解:当fWaitAll参数为真时,在所有被等待的

对象都成为有信号之前,被等待的任何对象都

不会被重置为无信号。也就是说,

WaitForMultipleObjects除非能取得所有指定

对象的所有权,它不会取得单个对象的所有权,否则,可能会发生死锁。线程同步:互斥量¤

互斥量(Mutex)非常类似于临界区,使用互斥量能够确保线程拥有对单个资源的互斥访问权。不同的是,临界区属于用户模式对象,而互斥量属于内核模式对象,因此互斥量的运行速度比临界区要慢。但我们知道临界区只能用于同一个进程中的线程间同步,而互斥量可以用来同步多个进程间的数据访问。另外,临界区无法设定等待超时,而互斥量可以。线程同步:互斥量¤

一个互斥对象包含一个使用数量,一个线程ID和一个递归计数器。ID用于标识系统中的哪个线程当前拥有互斥对象。递归计数器用于指明该线程拥有互斥对象的次数。¤

互斥对象有许多用途,属于最常用的内核对象

之一。通常来说,它们用于保护由多个线程访

问的内存块。如果多个线程要同时访问内存块,内存块中的数据就可能遭到破坏。互斥对象能

够保证访问内存块的任何线程拥有对该内存块

的独占访问权,这样就能够保证数据的完整性。线程同步:互斥量¤

互斥量相关的API函数:CreateMutex:创建互斥量对象。OpenMutex:打开指定名称的互斥量对象。WaitForXxxx函数:获取互斥对象的拥有权。ReleaseMutex:释放对互斥对象的拥有权。CloseHandle:关闭互斥对象句柄。线程同步:互斥量¤

要使用互斥量对象,一个进程必须首先使用CreateMutex函数来创建一个互斥量对象。函数原型如下:线程同步:互斥量¤

和很多用于创建内核对象的函数一样,

CreateMutex函数的参数lpMutexAttributes指向一个SECURITY_ATTRIBUTES结构,传递

NULL值将使用缺省的安全描述。¤

参数bInitialOwner指出创建互斥量的线程是否要成为该互斥量的最初所有者,TRUE表示线程将拥有互斥量,因此互斥量将处于无信号状态,FALSE将使互斥量处于有信号状态。¤

参数lpName为互斥量的名称,如果该名称标识的互斥量已经存在,则函数返回该互斥量的句柄,传递NULL值将创建一个无名的互斥量对象。线程同步:互斥量¤

另一种得到互斥量对象句柄的方式是调用OpenMutex函数。函数原型如下:¤

参数dwDesiredAccess可以取值SYNCHRONIZE或MUTEX_ALL_ACCESS。参数bInheritHandle指明了该进程创建的子进程能否继承此互斥量。lpName为互斥量的名字,lpName不得为NULL。线程同步:互斥量¤函数ReleaseMutex用于释放线程对互斥量对象的拥有权。没有拥有互斥量的线程调用该函数将失败。¤值得一提的是,互斥量讲的是线程对它的拥有权,有意义的不仅仅是它是否有信号,比如说一个线程调用

WaitForSingleObject返回后拥有了对一个互斥量的拥有权,那么在这个线程调用ReleaseMutex之前,另一个线程调用WaitForSingleObject函数将被阻塞,因为此时互斥量是无信号的。¤但如果在同一线程中调用Wait函数,比如在已经拥有了互斥量的线程中,在调用ReleaseMutex之前再进行几次Wait调用,那么虽然调用Wait函数的时候该互斥量无信号,但正如我们讲的,这里的Wait函数是为了线程获取对互斥量的拥有权,而此时线程已经拥有了该互斥量,因此随后的调用将立即返回成功,而不会导致线程阻塞。线程同步:互斥量¤互斥量的这种行为和事件对象等是不同的,如果拥有互斥量的线程调用了多次Wait函数,那么互斥量中的计数会递增,随着相应数目的ReleaseMutex的调用,计数减少为0,并释放对互斥量的拥有。¤奇怪的是,同一个线程不论调用了几次Wait函数,只要调用一次ReleaseMutex函数,就会释放对互斥量的拥有权,从拥有权的角度考虑,这种情况是合理的,但从使用线程的计数考虑,似乎并不合理。这种情况同一些文档的讲述也不同,因此,最好不要依赖这个事实。¤事实上,一个线程中对一个互斥量连续调用多个Wait函数是没有意义的。线程同步:互斥量¤

废弃的互斥量。前面讨论WaitForSingleObject和WaitForMultipleObjects函数的返回值时,我们层几次提到废弃的互斥量这个概念。所谓废弃,是指当一个线程拥有了一个互斥量之后,在没有调用

ReleaseMutex之前就被终结了。¤比如该线程被外部调用TerminateThread强行终结,或者该线程中遗漏了对ReleaseMutex的调用,那么这时将没有一个线程可以调用ReleaseMutex函数来将其置为有信号的状态。¤为了不妨碍其它线程的工作,这时系统会将互斥量自动置为有信号。这时对于一个等待成功的

WaitForSingleObject函数,其返回值将是

WAIT_ABANDONED而不是WAIT_OBJECT_0。范例:互斥量应用¤

练习:将上一讲中的CritSecs范例代码中使用

CriticalSection的内容改为使用互斥量对象实现。要求自主完成。线程同步:事件对象¤

事件(Event)对象是同步对象中最简单的形式,它用来发信号表示某一操作已经完成。有两种事件对象:人工重设事件(Manual

Reset

Event)和自动重设事件(Auto

Reset

Event)。¤

人工重设事件也叫做通知事件(NotificationEvent),它可以用于同时向几个等候线程发信号,也就是说可以有多个线程等待某一事件的发生,当事件发生时,所有等候线程的WaitXxxx函数均成功返回。¤

自动重设事件也叫做同步事件(SynchronizationEvent),它用于向一个等候线程发信号,表示某一操作已经完成。如果有多个线程等待该事件,系统将排队每个等候,当事件变为有信号时,最先等待的一个线程会等待成功,并且立即将事件重置为无信号,这样其它线程就得继续等待。线程同步:事件对象¤

事件最常用于一个线程进行初始化工作后,发信号给另一个线程,让其完成剩余的工作。¤

初始化线程将事件设置为无信号状态后开始进行初始化。当初始化完成后,线程把事件设置为有信号。¤

工作线程开始执行时,它立刻自我挂起,等待事件变为有信号。当初始化线程把事件设置为有信号后,工作线程就醒来执行必要的工作。线程同步:事件对象¤

举例如下:一个进程运行了两个线程,一个线程用于访问文件,另一个线程用于处理文件数据。当第一个线程从文件读取数据到内存缓冲区后,通过一个事件对象通知正在等待的第二个线程,告诉它可以处理数据了。当第二个线程完成了对数据的处理后,它可能需要再给第一个线程发信号,请求第一个线程从文件中读取下一块数据。线程同步:事件对象¤

事件对象相关的API函数:CreateEvent:OpenEvent:SetEvent:ResetEvent:PulseEvent:创建一个事件对象打开一个存在的事件对象将事件置为有信号将事件置为无信号将事件置为有信号后立即重置为无信号。以及WaitForXxxx函数和CloseHandle等。线程同步:事件对象¤

CreateEvent函数原型如下:¤第一个参数仍然是安全描述,通常传递NULL使用缺省值。参数bManualReset指明事件类型为人工重置还是自动重置。参数bInitialState指明了事件的初始状态为有信号还是无信号。参数lpName指明事件的名称,传递NULL则创建一个无名的事件对象。线程同步:事件对象¤

其它函数OpenEvent函数和OpenMutex完全类似,根据事件对象名称获取对象句柄,并可指定访问权限和子进程对句柄的继承属性。SetEvent用于设置事件状态为有信号。ResetEvent用于人工重设事件,对事件句柄调用

SetEvent函数后,一个人工重设事件保持有信号直到ResetEvent函数调用。PulseEvent相当于SetEvent和ResetEvent的组合,对于人工重设事件,PulseEvent使事件有信号,导致同时等候多个线程被唤醒,之后事件被重置为无信号。对于自动重置事件,每次只唤醒一个等候线程。范例:DocStats应用程序¤

DocStats应用程序范例演示了如何使用自动重设事件对象。该程序使用如下的用户界面:范例:DocStats应用程序¤

DocStats分析指定的文件,显示该文件中的字符数、单词数和行数。通过界面的Browse按钮选择一个文件,但它只能处理ANSI文件,对于Unicode文件或二进制文件将返回错误的结果。¤

当点击Calculate

Document

Statistics按钮后,

DocStats创建了三个线程,每个线程统计一种数据(字符、单词和行)。这些线程创建后被挂起。接着DocStats打开指定文件,将它的前

1024个字节装入缓冲区后,通过事件对象通知3个线程,让它们恢复执行来处理数据。线程同步:可等的计时器¤

可等的计时器(WaitableTimer)是在某个时间或按规定的间隔时间使自己有信号的内核对象。它们通常用来在某个时间执行某个操作。¤

类似于事件对象,可等的计时器也包含人工重置的定时器或自动重置的定时器。当发出人工重置的定时器信号通知时,等待该定时器的所有线程均变为可调度线程。当发出自动重置的定时器信号通知时,只有一个等待的线程变为可调度线程。线程同步:可等的计时器¤

可等的计时器相关的API函数:CreateWaitableTimer:创建可等的计时器OpenWaitableTimer:打开指定名称的可等计时器SetWaitableTimer:可等的计时器对象总是在无信号状态中创建,通过该函数来告诉定时器想在何时让它成为有信号状态。CancelWaitableTimer:用于取出定时器的句柄并将它撤消,这样,除非接着调用SetWaitableTimer函数以便重新设置定时器,否则定时器决不会进行报时。线程同步:可等的计时器¤

CreateWaitableTimer函数和

OpenWaitableTimer函数的参数及其含义和事件对象的对应函数完全一致。¤

SetWaitableTimer比较复杂,函数原型如下图所示:线程同步:可等的计时器¤

SetWaitableTimer的参数hTimer为可等计时器的句柄。参数pDueTime指示计时器应何时启动,它接收一个LARGE_INTEGER结构的指针,LARGE_INTEGER相当于一个64位的整数。它必须是一个表示格林威治标准时间(UTC)的值。参数lPeriod指示计时器多长时间启动一次,lPeriod为以毫秒计的时间间隔。¤

下图所示的例子显示了如何让一个定时器在

2002年1月1日的下午1点钟首次启动,而后每隔6小时启动一次。线程同步:可等的计时器线程同步:可等的计时器¤上图的代码中首先初始化了一个SYSTEMTIME结构,并将时间设置为本地时间,之后调用

SystemTimeToFileTime函数将其转化为一个

FILETIME结构。由于Set

温馨提示

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

评论

0/150

提交评论