第九章:用内核对象进行线程同步(二) .
2011-10-11 22:19
357 查看
7 互斥量内核对象用来确保一个线程独占对一个资源的访问.
■ 组成:
①使用计数
②线程ID:标示当前占用这个互斥量的是系统中那个线程
③递归计数: 表示这个线程占用该互斥量的次数.
■ 与关键代码段的区别:
● 互斥量是内核模式下的,而关键代码段是用户模式下的.也意味着关键代码段比 互斥量要快.
● 不同进程中的线程可以访问同一个互斥量.
■ 互斥量的规则:
● 如果线程ID为0(即无效线程ID),那么该互斥量不为任何线程锁占有,它处于触 发状态
● 如果线程ID为非0,那么有一个线程已经占用了该互斥量,它处于未触发状态
● 与所有其他内核对象不同,OS对互斥量进行了特殊处理,欲行它们违反一些常 规的规则.
■ 互斥量的一些操作
● 创建
HANDLE CreateMutex(
PSECUTRY_ATTRIBUTES psa,
BOOL bInitialOwner,//互斥量的初始状态(TRUE表示处于未触发状态)线程ID //和递归计数分别设为当前线程ID和1
PCTSTR pszName);
HANDLE CreateMutexEx(
PSECUTRY_ATTRIBUTES psa,
PCTSTR pszName,
DWORD dwFlags,//相当于bInitialOwner(0表示 //FALSE,CREATE_MUTEX_INITIAL_OWNER等价于TRUE
DWORD dwDesiredAccess);
● 获取互斥量句柄
HANDLE OpenMutex(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
PCTSTR pszName);
● 释放互斥量,使其处理可触发状态.
BOOL ReleaseMutex(HANDLE hMutex);
函数工作方式:使得对象的递归计数减一.如果线程成功地等待了互斥量对象不止一次,那么线程必须调用相同次数ReleaseMutex使得递归计数为0,并且此时线程ID也设为0.这样就出发了对象.
互斥量与其他内核对象的区别:
加入线程试图等待一个未触发的互斥量对象,线程就会进入等待状态,但是如果系统检查想要获得互斥量的线程的ID与互斥量对象内部记录的线程ID相等那么此时系统也会让线程保持可调度状态--即使该互斥量尚未触发.
■ 互斥量与其它内核对象不同的根本原因是"所有权"问题:也就是说,其它内核对象不会记住自己是哪个线程等待成功的.这就使得他即使在未触发状态下也能为线程所得.
这样就会出现一种情况是,如果占用互斥量的线程在释放互斥量之前终止,那么互斥量将出现"遗弃".
由于系统记录着所有的互斥量和线程内核对象,所以他有能力使得被遗弃的互斥量重新变为触发状态.这样其他的等待互斥量的线程就可以获取到互斥量的资源.但是这里有一个小小的不同:处于等待的函数不在返回通常的WAIT_OBJECT_0而是返回WAIT_ABANDONED.
■ 互斥量和关键段的区别:
特征 | 互斥量 | 关键段 |
是否能跨进程使用 | 慢 | 快 |
声明 | HANDLE hmtx | CRITICAL_SECTION cs |
初始化 | Hmtex = CreateMutex(NULL,FALSE,NULL) | InitializeCriticalSection(&cs) |
清理 | CloseHandle(hmtx) | DeleteCriticalSection(&cs) |
无限等待 | WaitForSignalObject(hmtx,INFINITE) | EnterCriticalSection(&cs) |
0等待 | WaitForSignalObject(hmtx,INFINITE) | TryEnterCriticalSection(&cs) |
任意时间长度等待 | WaitForSingleObject(hmtx,dwMilliseconds) | 不支持 |
释放 | ReleaseMutex(hmtx) | LeaveCriticalSection(&cs) |
能否同时等待其他内核对象 | 是(使用WaitForMultipleObject或者类似的函数) | 否 |
对象 | 何时处于未触发状态 | 何时处于触发状态 | 成功等待的副作用 |
进程 | 进程仍在运行 | 进程终止时(ExitProcess或者TerminateProcess) | 没有 |
线程 | 线程仍在运行时 | 线程终止的时候(Exit(Terminate)Thread | 没有 |
作业 | 作业尚未超时时 | 作业超时时 | 没有 |
文件 | 有待处理的I/O请求的时 | I/O请求完成时 | 没有 |
控制台输入 | 没有输入时 | 有输入时 | 没有 |
文件变更通知 | 文件没有变更通知时 | 文件系统检测到变更时 | 重置通知 |
自动重置事件 | ResetEvent,PulseEvent或等待成功时 | SetEvent/PulseEvent被调用时 | 重置事件 |
手动重置事件 | ResetEvent,PulseEvent | SetEvent/PulseEvent被调用时 | 没有 |
自动重置可等待计时器 | CancelWaitableTimer或者等待成功的时候 | 时间到时(SetWaitableTimer) | 重置计数器 |
手动重置可等待计时器 | CancelWaitableTimer | 时间到时(SetWaitableTimer) | 没有 |
信号量 | 等待成功时 | 计数大于0(ReleaseSemaphore) | 计数器减一 |
互斥量 | 等待成功的时候 | 不为线程占用时(ReleaseMutex) | 把所有权交给线程 |
关键段(用户模式) | 等待成功时((Try)EnterCriticalSection) | 不为线程占用时(LeaveCriticalSection) | 把所有权交给线程 |
SRWLock(用户模式) | 等待成功时(AcquireSRWLock(Exclusive)) | 不为线程占用时(ReleaseSRWLock(Exclusive)) | 把所有权交给线程 |
条件变量(用户模式) | 等待成功时(SleepConditionVariable*) | 被唤醒时(Wake(All)ConditionVariable) | 没有 |
8 异步设备I/O:允许线程开始读取操作或者写操作,而不需等待读取操作或者等待操作完成.设备对象时可同步的内核对象,即我们可以调用WaitForSignalObject,并传入文件句柄、套接字、通信端口等等。当系统执行异步的I/O的时候,设备处于为触发状态,一旦操作完成,系统将变成触发状态。这样线程就知道操作已经完成了,线程就可以继续执行。
函数:DWORD WaitForInputIdle(
HANDLE hProcess,
DWORD dwMillseconds);
先挂起当前进程,然后等待hProcess标示的进程,直到创建应用程序第一个窗口的(也就是hProcess标示的进程)线程中没有待处理的输入为止.
线程也可以调用MsgWaitForMultipleObjects或MsgWaitForMultipleObjsEx,使得线程等待需要自己处理的消息.
DWORD MsgWaitForMultipleObjects(
DWORD dwCount,
PHANDLE phObjects,
BOOL bWaitAll,
DWORD dwMilliseconds,
DWORD dwWakeMask);
DWORD MsgWaitForMultipleObjectsEx(
DWORD dwCount,
PHANDLE phObjects,
DWORD dwMilliseconds,
DWORD dwWakeMask,//标示是哪类消息.
DWORD dwFlags);
这些函数与WaitForMultipleObjects类似,不同之处在于,不仅内核对象被触发的时候调用线程会变成可调度状态,而且当窗口消息需要被派送到一个由调用线程创建的窗口时,他们也会变成可调度状态.
创建窗口的线程和执行与用户界面相关的任务线程都不应该使用WaitForMultipleObjects,而应该使用MsgWaitForMultipleObjectsEx.因为前者会妨碍线程对用户在用户界面上的操作进行响应.
9. 当调试器开始执行的时候,会将自己附着到被调试的程序.然后调试器只是在一边闲着,等待操作系统通知它有与被调试程序相关的时间发生.调试器通过WaitForDebugEvent函数类等待这些时间:
BOOL WaitForDebugEvent(
PDEBUG_EVENT pde,
DWORD dwMillsecond);
当调试器调用这个函数的时候,调试器的线程会挂起,系统通过让WaitForDebugEvent返回的方式,来通知调试器有调试事情发生.参数pde指向的结构包含了与刚才发生的调试事件有关的信息.
10. SignalObjectAndWait函数通过一个原子操作来触发内核对象并等待另一个内核对象:
DWORD SignalObjectAndWait(
HANDLE hObjectToSignal,//标示的必须是一个互斥量、信号量或者事件.
HANDLE hObjectToWaitOn,//标示互斥量、信号量、事件、计时器、进程、线程、 //作业、控制台输入以及变更通知.
DWORD dwMilliseconds,//最多应该花多长时间来等待对象触发
BOOL bAlertable); //当线程处于等待状态时,是否能够对添加到队列中的异步 //过程调用进行处理
hObjectToSignal:任何其它类型对象将导致函数返回WAIT_FAILED.这时使用GetLastError会返回ERROR_INVALID_HANDLE.该函数内部会检查对象的类型并分别执行与ReleaseMutex、ReleaseSemaphore(使用计数减一)或者SetEvent等价的操作.
返回值:WAIT_OBJECT_0,WAIT_OBJECT_TIMEOUT,WAIT_FAILED,WAIT_ABANDONED或者WAIT_IO_COMPLETION.
函数的优点:花费时间短.
10. Vista提供了一组新的等待链遍历(简称WCT)API,这些函数可以让我们列出所有锁,并检测进程内部,甚至是进程之间的死锁.
可能的锁 | 描述 |
关键段 | Windows会记录哪个线程正在占用哪个关键段 |
互斥量 | Windows户记录哪个线程正在占用哪个互斥量,即使被遗弃 |
进程和线程 | Window会记录哪个线程正在等待进程终止或者线程终止 |
SendMessage调用 | 知道那个线程正在等待SendMessage调用返回时很重要的 |
COM初始化和调用 | Windows会记录对CoCreateInstance的调用以及对COM对象的方法的调用 |
高级本地过程调用 | 在windows Vista中,作为新的未公开的内核进程间通信机制,ALPC已经取代了本地过程调用. |
相关文章推荐
- <<Windows核心编程(第五版)>>第九章用内核对象进行线程同步:9.3事件内核对象
- 第九章:用内核对象进行线程同步(一)
- 第九章 使用内核对象进行线程同步
- 第九章:用内核对象进行线程同步
- Windows核心编程学习九:利用内核对象进行线程同步
- 《Windows核心编程》——九 用内核对象进行线程同步
- 《Windows核心编程系列》八谈谈用内核对象进行线程同步
- Windows核心编程:用内核对象进行线程同步
- 进程与线程(六)用内核对象进行线程同步(下)
- 第9章 用内核对象进行线程同步(4)_死锁(DeadLock)及其他
- 摘自windows核心编程之用内核对象进行线程同步
- Windows核心编程--用内核对象进行线程同步(一)
- windows核心编程-9.用内核对象进行线程同步
- Windows核心编程 用内核对象进行线程同步
- (摘自windows核心编程之用内核对象进行线程同步)
- Windows核心编程笔记(九)使用内核对象进行线程同步
- <<windows核心编程>>读书笔记---第9章 内核对象进行线程同步
- (摘自windows核心编程之用内核对象进行线程同步)
- 《Windows核心编程系列》八谈谈用内核对象进行线程同步