7.等待定时器内核对象
等待定时器内核对象是在某个时间或某个规定的时间间隔内发出自己的信号通知内核对象。它们通常是用在:在某人时间执行某个操作。
创建定时器内核对象:
HANDLE CreateWaitableTimer(
PSECURITY_ATTRIBUTES psa,
BOOL fManualReset, PCTSTR pszName);
关于参数以前都介绍过了,在此不述。
创建定时器后,定时器是在未通知状态,注意,定时器总是在未通知状态下被创建,完毕之后,需要设定何时让该对象变为已通知状态,这就需要用到:SetWaitableTimer函数:
BOOL SetWaitableTimer(
HANDLE hTimer,
const LARGE_INTEGER *pDueTime,
LONG lPeriod,
PTIMERAPCROUTINE pfnCompletionRoutine,
PVOID pvArgToCompletionRoutine,
BOOL fResume);
其中,hTimer是指定时器内核对象的句柄。pDueTime与lPeriod是一起使用,pDueTimer表示的是定时器何时第一次报时,lPeriod表示是隔多长时间报时一次。至于详细的参数设置与例子,书上讲得比较清楚。
8.信标内核对象
信标内核对象是用来对资源进行计数的一个内核对象。它除了包含一个内核对象所共有的psa属性以外,还有两个带符号的32位值的属性,一个是最大资源数量,一个是当前资源数量。最大资源数量用于标识信标能够控制的资源的最大数量,而当前资源数量则用于标识当前可以使用的资源的数量。
何时能使用信标内核对象,比如,我开发一个服务器进程,在这个进程中,我分配了五个可以供客户使用的缓冲区,这样服务器能处理5个客户机的请求。开始时,没有客户机请求,那么,最大资源数量为5,当前资源数量为0。现在,有三个客户请求缓冲区,服务器进程可以分配三个缓冲区为这三个客户使用,服务器创建三个线程,并使这三个线程处于可调度状态。
但是,书上有一段话不太明白,就是:
当客户机的请求被接受时,当前资源数量就递增,当客户机的请求被提交给服务器的线程池时,当前资源数量就递减。
究竟被接受与提交给服务器线程池有什么区别呢?为什么会导致一会儿又递增,一会儿又递减?
书上还指出了信标的使用规则:
• 如果当前资源的数量大于0,则发出信标信号。
• 如果当前资源数量是0,则不发出信标信号。
• 系统决不允许当前资源的数量为负值。
• 当前资源数量决不能大于最大资源数量。
还有:不要将信标内核对象的使用数量与当前资源数量混为一谈。
创建信标内核对象:
HANDLE CreateSemaphore(
PSECURITY_ATTRIBUTE psa,
LONG lInitialCount,
LONG lMaximumCount, PCTSTR pszName);
通过调用ReleaseSemaphore函数,可以对当前资源数量进行递增:
BOOL ReleaseSemaphore(
HANDLE hsem,
LONG lReleaseCount, PLONG plPreviousCount);
9.互斥内核对象
互斥内核对象能够保证线程拥有对单个对象的访问权。互斥对象包含一个使用计数,一个线程ID,一个递归计数器。他的功能与关键代码段相同。不同的是一个属于内核对象,一个属于用户方式对象,另外一个不同的是互斥内核对象可以让不同的进程中的线程访问同一资源。
互斥对象的使用规则:
• 如果线程I D是0(这是个无效I D),互斥对象不被任何线程所拥有,并且发出该互斥对象的通知信号。
• 如果I D是个非0数字,那么一个线程就拥有互斥对象,并且不发出该互斥对象的通知信号。
• 与所有其他内核对象不同, 互斥对象在操作系统中拥有特殊的代码,允许它们违反正常的规则(后面将要介绍这个异常情况)。
关于互斥对象的函数:
创建互斥对象:
HANDLE CreateMutex(
PSECURITY_ATTRIBUTES psa,
BOOL fInitialOwner,
PCTSTR pszName);
打开互斥对象:
HANDLE OpenMutex(
DWORD fdwAccess,
BOOL bInheritHandle,
PCTSTR pszName);
fInitialOwner控制着互斥对象的初始状态,如果传递FALSE,则表示互斥对象的线程ID和递归计数均被设为0. 它意味着资源不被任何线程所占有,因此它要发出通知信号。
如果传递TRUE,那么对象的线程ID被设置成调用它线程的ID,递归计数设为1,资源被本线程占有,不发出通知信号。
一旦线程成功等待到一个互斥对象,它就已经拥有了对资源的独占访问权,任何要访问该资源的线程均被置于等待中。
当一个线程不需要对该资源的访问权时,必须释放互斥对象:
BOOL ReleaseMutex(HANDLE hMutex);
互斥对象不同于其它对象的地方:
因为它有线程所有权的概念。没有哪个对象能够记住哪一个线程成功的等待到该对象,只有互斥对象能够保持跟踪。
书中列了两张表,一张是互斥对象与关键代码的区别,一张是线程同步对象速查表。比较有用,COPY至此:
表9-1 互斥对象与关键代码段的比较
特性 |
互斥对象 |
关键代码段 |
运行速度 |
慢 |
快 |
是否能够跨进程边界来使用 |
是 |
否 |
声明 |
HANDLE hmtx; |
CRITICAL_SECTION cs; |
初始化 |
h m t x = C r e a t e M u t e x(N U L L,FA L S E,N U L L); |
I n i t i a l i z e C r i t i c a l S e c t i o n ( & e s ); |
清除 |
C l o s e H a n d l e(h m t x); |
D e l e t e C r i t i c a l S e c t i o n(& c s); |
无限等待 |
Wa i t F o r S i n g l e O b j e c t(h m t x , I N F I N I T E); |
E n t e r C r i t i c a l S e c t i o n(& c s); |
0等待 |
Wa i t F o r S i n g l e O b j e c t Tr y(h m t x , 0); |
E n t e r C r i t i c a l S e c t i o n(& c s); |
任意等待 |
Wa i t F o r S i n g l e O b j e c t(h m t x , d w M i l l i s e c o n d s); |
不能 |
释放 |
R e l e a s e M u t e x(h m t x); |
L e a v e C r i t i c a l S e c t i o n(& c s); |
是否能够等待其他内核对象 |
是(使用Wa i t F o r M u l t i p l e O b j e c t s或类似的函数) |
否 |
表9-2 内核对象与线程同步之间的相互关系
对象 |
何时处于未通知状态 |
何时处于已通知状态 |
成功等待的副作用 |
进程 |
当进程仍然活动时 |
当进程终止运行时(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) |
无 |
线程 |
当线程仍然活动时 |
当线程终止运行时(E x i t T h r e a d,Te r m i n a t e T h r e a d) |
无 |
作业 |
当作业的时间尚未结束时 |
当作业的时间已经结束时 |
无 |
文件 |
当I / O请求正在处理时 |
当I / O请求处理完毕时 |
无 |
控制台输入 |
不存在任何输入 |
当存在输入时 |
无 |
文件修改通知 |
没有任何文件被修改 |
当文件系统发现修改时 |
重置通知 |
自动重置事件 |
R e s e t E v e n t , P u l s e - E v e n t或等待成功 |
当调用S e t E v e n t / P u l s e E v e n t时 |
重置事件 |
人工重置事件 |
R e s e t E v e n t或P u l s e E v e n t |
当调用S e t E v e n t / P u l s e E v e n t时 |
无 |
自动重置等待定时器 |
C a n c e l Wa i t a b l e Ti m e r或等待成功 |
当时间到时(S e t Wa i t a b l e Ti m e r) |
重置定时器 |
人工重置等待定时器 |
C a n c e l Wa i t a b l e Ti m e r |
当时间到时(S e t Wa i t a b l e Ti m e r) |
无 |
信标 |
等待成功 |
当数量> 0时(R e l e a s e S e m a p h o r e) |
数量递减1 |
互斥对象 |
等待成功 |
当未被线程拥有时(R e l e a s e互斥对象) |
将所有权赋予线程 |
关键代码段(用户方式) |
等待成功((Tr y)E n t e r C r i t i c a l S e c t i o n) |
当未被线程拥有时(L e a v e C r i t i c a l S e c t i o n) |
将所有权赋予线程 |