若干种内核对象,包括进程,线程和作业。可以将所有这些内核对象用于同步目的。对于线程同步来说,这些内核对象中的每种对象都可以说是处于已通知或未通知的状态之中。
例如::当进程正在运行的时候,进程内核对象处于未通知状态,当进程终止运行的时候,它就变为已通知状态。进程内核对象中是个布尔值,当对象创建时,该值被初始化为FALSE(未通知状态)。当进程终止运行时,操作系统自动将对应的对象布尔值改为TRUE,表示该对象已经得到通知。当线程终止运行时,操作系统会自动将线程对象的状态改为已通知状态。因此,可以将相同的方法用于应用程序,以确定线程是否不再运行。
1、等待函数。
等待函数可使线程自愿进入等待状态,直到一个特定的内核对象变为已通知状态为止:WaitForSingleObject
2、成功等待的副作用。
当事件对象变为已通知状态时,函数就会发现这个情况,并将WAITOBJECT0返回给调用线程。但是就在函数返回之前,该事件将被置为未通知状态,这就是成功等待的副作用。(空谈啊,空谈啊...)
如果多个线程等待单个内核对象,那么当该对象变成已通知状态时,系统究竟决定唤醒哪个线程呢?
等待了最长时间的线程将得到该对象。系统会让线程暂停运行。如果一个线程等待一个对象,然后该线程暂停运行,那么系统就会忘记该线程正在等待该对象。这是一个特性,因为没有理由为一个暂停运行的线程进行调度。当后来该线程恢复运行时,系统将认为该线程刚刚开始等待该对象。
3、事件内核对象
在所有的内核对象中,事件内核对象是个最基本的对象。它们包含一个使用计数(与所有内核对象一样),一个用于指明该事件是个自动重置的事件还是一个人工重置的事件的布尔值,另一个用于指明该事件处于已通知状态还是未通知状态的布尔值。注意:
(1)、人工重置事件与自动重置事件的区别。
事件能够通知一个操作已经完成。有两种不同类型的事件对象。一种是人工重置的事件,另一种是自动重置的事件。
当人工重置的事件得到通知时,等待该事件的
所有线程均变为可调度线程。
当一个自动重置的事件得到通知时,等待该事件的线程中
只有一个线程变为可调度线程。
当线程成功地等待到该对象时,自动重置的事件就会自动重置到未通知状态。这就是自动重置的事件如何获得它们的名字的方法。通常没有必要为自动重置的事件调用ResetEvent函数,因为系统会自动对事件进行重置。
(2)、使用createvent创建。注意参数:创建的是人工还是自动,匿名还是命名事件。对于命名事件只能创建一个。
(3)、PulseEvent函数使得事件变为已通知状态,然后立即又变为未通知状态。
4、等待定时器内核对象
等待定时器是在
某个时间或按规定的间隔时间发出自己的信号通知的内核对象。它们通常用来在某个时间执行某个操作。
若要创建等待定时器,只需要调用CreateWaitableTimer函数:
注意:
(1)、进程可以获得它自己的与进程相关的现有等待定时器的句柄,方法是调用OpenWaitableTimer函数:
(2)、与事件的情况一样有人工重置的定时器或自动重置的定时器。当发出人工重置的定时器信号通知时,等待该定时器的
所有线程均变为可调度线程。当发出自动重置的定时器信号通知时,只有
一个等待的线程变为可调度线程。
(3)、等待定时器对象总是在未通知状态中创建。必须调用SetWaitableTimer函数来告诉定时器你想在何时让它成为已通知状态:每次调用SetWaitableTimer函数,都会在设置新的报时条件之前撤消定时器原来的报时条件
(4)、CancelWaitableTimer函数:这个简单的函数用于取出定时器的句柄并将它撤消,这样,除非接着调用SetWaitableTimer函数以便重新设置定时器,否则定时器决不会进行报时。
(5)、与用户定时器(用SetTimer函数进行设置)进行比较:
①、它们之间的最大差别是,用
户定时器需要在应用程序中设置许多附加的用户界面结构,这使定时器变得资源更加密集。
②、等待定时器属于内核对象,这意味着它们可以供多个线程共享,并且是安全的。用户定时器能够生成
WM_TIMER消息,这些消息将返回给调用SetTimer(用于回调定时器)的线程和创建窗口(用于基于窗口的定时器)的线程。因此,当用
户定时器报时的时候,只有一个线程得到通知。另一方面,多个线程可以在等待定时器上进行等待,如果定时器是个人工重置的定时器,则可以
调度若干个线程。 ③、WM_TIMER消息始终属于最低优先级的消息,当线程的队列中没有其他消息时,才检索该消息。等待定时器的处理方法与其他内核对象没有什么差别,如果定时器发出报时信息,而你的线程正在等待之中,那么你的线程就会醒来。
5、信标内核对象
信标内核对象
用于对资源进行计数。它们与所有内核对象一样,包含
一个使用数量,但是它们也包含另外
两个带符号的3 2位值,一个是最大资源数量,一个是当前资源数量。最大资源数量用于标识信标能够控制的资源的最大数量,而当前资源数量则用于标识当前可以使用的资源的数量。可以建立2 147 483 647个资源。
信标的使用规则如下:
• 如果当前资源的数量大于0,则发出信标信号。
• 如果当前资源数量是0,则不发出信标信号。
• 系统决不允许当前资源的数量为负值。
• 当前资源数量决不能大于最大资源
创建:通过createsemaphore创建
当向信标申请一个资源时,操作系统就要检查是否有这个资源可供使用,同时将可用资源的数量递减,而不让另一个线程加以干扰。只有当资源数量递减后,系统才允许另一个线程申请对资源的访问权。
为了正确地说明这个问题,让我们来看一看应用程序是如何使用信标的。
比如说,
我正在开发一个服务器进程,在这个进程中,我已经分配了一个能够用来存放客户机请求的缓冲区。我对缓冲区的大小进行了硬编码,这样它每次最多能够存放5个客户机请求。如果5个请求尚未处理完毕时,一个新客户机试图与服务器进行联系,那么这个新客户机的请求就会被拒绝,并出现一个错误,指明服务器现在很忙,客户机应该过些时候重新进行联系。当我的服务器进程初始化时,它创建一个线程池,里面包含5个线程,每个线程都准备在客户机请求到来时对它进行处理。开始时,没有客户机提出任何请求,因此我的服务器不允许线程池中的任何线程成为可调度线程。但是,如果3个客户机请求同时到来,那么线程池中应该有3个线程处于可调度状态。使用信标,就能够很好地处理对资源的监控和对线程的调度,最大资源数量设置为5,因为这是我进行硬编码的缓冲区的大小。当前资源数量最初设置为0,因为没有客户机提出任何请求。当客户机的请求被接受时,当前资源数量就递增,当客户机的请求被提交给服务器的线程池时,当前资源数量就递减。
一些API函数对信标内核对象的操作
调用OpenSemaphore函数,另一个进程可以获得它自己的进程与现有信标相关的句柄:
通过调用ReleaseSemaphore函数,线程就能够对信标的当前资源数量进行递增:
6、互斥内核对象
互斥对象(m u t e x)内核对象能够确保线程拥有对单个资源的互斥访问权。实际上互斥对象是因此而得名的。互斥对象包含一个使用数量,一个线程I D和一个递归计数器。
特点:
(1)、互斥对象的行为特性与关键代码段相同,但是互斥对象属于内核对象,而
关键代码段则属于用户方式对象。这意味着互斥对象的运行速度比关键代码段要慢。但是这也意味着
不同进程中的多个线程能够访问单个互斥对象,并且这意味着线程在等待访问资源时
可以设定一个超时值。(2)、ID用于标识系统中的哪个线程当前拥有互斥对象,递归计数器用于指明该线程拥有互斥对象的次数。
(3)、它们用于保护由多个线程访问的内存块。如果多个线程要同时访问内存块,内存块中的数据就可能遭到破坏。互
斥对象能够保证访问内存块的任何线程拥有对该内存块的独占访问权,这样就能够保证数据的完整性。
互斥对象的使用规则如下:
• 如果线程ID是0(这是个无效I D),互斥对象不被任何线程所拥有,并且发出该互斥对象的通知信号。
• 如果I D是个非0数字,那么一个线程就拥有互斥对象,并且不发出该互斥对象的通知信号。
• 与所有其他内核对象不同, 互斥对象在操作系统中拥有特殊的代码,允许它们违反正常的规则
创建及一些常用API操作:
调用createmutex
另一个进程可以获得它自己进程与现有互斥对象相关的句柄:openmutex
释放:releasemutex,有多少次等待就有多少次释放调用。
当递归计数器到达0时,该线程I D也被置为0,同时该对象变为已通知状态。系统要查看是否有任何线程正在等待互斥对象。如果有,系统将“按公平原则”选定等待线程中的一个,为它赋予互斥对象的所有权。当然,这意味着线程I D被设置为选定的线程的I D,并且递归计数器被置为1。如果没有其他线程正在等待互斥对象,那么该互斥对象将保持已通知状态,这样,等待互斥对象的下一个线程就立即可以得到互斥对象。
没有一种对象能够记住哪个线程成功地等待到该对象,只有互斥对象能够对此保持跟踪。
互斥对象的释放:试图释放不是调用者拥有的互斥对象
当一个线程调用releasemutex函数时,该函数要查看调用线程的I D是否与互斥对象中的线程I D相匹配。如果两个I D相匹配,递归计数器就会像前面介绍的那样递减。如果两个线程的I D不匹配,那么releasemutex函数将不进行任何操作,而是将FALSE(表示失败)返回给调用者。
如果在释放互斥对象之前,拥有互斥对象的线程终止运行(使用E x i t T h r e a d、Te r m i n a t e T h r e a d、E x i t P r o c e s s或Te r m i n a t e P r o c e s s函数),那么互斥对象和正在等待互斥对象的其他线程将会发生什么情况呢?答案是,系统将把该互斥对象视为已经被放弃——拥有互斥对象的线程决不会释放它,因为该线程已经终止运行。(故不宜使用这些函数)
//////////////////////待续
posted on 2011-10-08 00:10
Yu_ 阅读(386)
评论(0) 编辑 收藏 引用 所属分类:
Windows程序设计