用于线程同步的内核对象:
1.互斥内核对象
2.事件内核对象
3.信号量内核对象
4.可等待的计时器内核对象
等待函数
在介绍线程同步内核对象之前,首先要了解等待函数。等待函数使得一个线程进入等待状态,直到指定的内核对象被触发为止。
DWORD WaitForSingleObject( HANDLE hHandle, //要等待的内核对象 DWORD dwMilliseconds //等待的时限 ); DWORD WaitForMultipleObjects( DWORD nCount, //希望检查的内核对象数量 const HANDLE *lpHandles, //指向希望检察的内核对象句柄数组 BOOL bWaitAll, //是否等待所有内核对象被触发 DWORD dwMilliseconds //等待的时限 );
如果等待的内核对象被触发,则返回值为WAIT_OBJECT_0;如果等待超时,则返回WAIT_TIMEOUT;如果传入了无效参数,则返回WAIT_FAILED。
对于WaitForMutipleObjects,如果bWaitAll是false,那么只要任何一个对象被触发,函数就立即返回,返回值为WAIT_OBJECT_0+触发的对象在对象数组中的索引。
1.互斥内核对象
HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, //安全属性 BOOL bInitialOwner, //是否获得所有权 LPCTSTR lpName //名称 ); BOOL ReleaseMutex(HANDLE hMutex); //释放互斥对象所有权 DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);//请求互斥对象的所有权
互斥对象能够确一个保线程独占一个资源的访问权。包含一个使用计数、线程ID和递归计数器。互斥对象有以下两个特点:
(1)谁拥有谁释放。每个互斥对象都有一个线程ID,用来标识所有者的线程(当互斥对象未被任何一个线程拥有是,线程ID为0),当调用 ReleaseMutex 释放互斥对象时,系统会对互斥对象的线程ID和当前线程ID进行检查,如果相同则释放,如果不同则不释放。
(2)可以重复获得请求。当调用WaitForSingleObject请求互斥对象所有权时,如果请求线程的ID与该互斥对象的线程ID相同,仍能获得请求,此时,互斥对象的递归计数器+1,当调用 ReleaseMutex时,互斥对象的递归计数器-1,当递归计数器的值为0时,将互斥对象的线程ID设置为0,同时,将互斥对象变为已触发状态。
2.事件内核对象
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, //安全属性 BOOL bManualReset, //创建人工重置对象(TRUE),还是自动重置对象(FALSE) BOOL bInitialState, //初始状态 LPCTSTR lpName ); BOOL SetEvent(HANDLE hEvent); BOOL ResetEvent(HANDLE hEvent);
事件内核对象保证当一个事件发生时,等待的操作才开始执行。包含一个使用计数、一个用来表示事件是自动重置还是手动重置的布尔值和一个表示事件是否发生的布尔值。
人工重置对象和自动重置对象区别:
当人工重置的事件对象得到通知时,等待该事件对象的所有线程均变为可调度线程,在线程得到该事件对象之后,操作系统并不会将该事件对象设置成未触发状态,除非显示地调用ResetEvent函数将其设置为无信号状态,否则该对象会一直是触发状态。
当自动重置的事件对象得到通知时,等待该事件对象的线程中只有一个线程变为可调度线程,同时操作系统会将该事件对象设置为未触发状态,这样,当所保护的代码执行完毕之后,需要调用SetEvent函数将该事件对象设置为触发状态。
3.信号量内核对象
CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, //初始计数 LONG lMaximumCount, //最大计数 LPCTSTR lpName ); BOOL ReleaseSemaphore( HANDLE hSemaphore, //信号量句柄 LONG lReleaseCount, //释放的资源个数 LONG plPreviousCount //当前资源计数的原始值 );
信号量内核对象用来对资源进行计数。包含一个使用计数、一个资源最大计数和当前资源计数。
如果当前资源计数大于0,则信号量处于触发状态;如果当前资源计数等于0,则信号量处于未触发状态。
可等待的计时器可以指定某个时间触发或者每个一段时间触发一次。
fResume对于支持挂起和继续执行的计算机,fResume为TRUE时,计时器触发时机器结束挂起,并唤醒所有等待该计时器的线程;fResume为FALSE时,计时器触发时,机器不被唤醒。
HANDLE CreateWaitableTimer( LPSECURITY_ATTRIBUTES lpTimerAttributes, BOOL bManualReset, //表示创建手动重置计时器(TRUE)还是自动重置计时器(FALSE) LPCTSTR lpTimerName ); BOOL SetWaitableTimer( HANDLE hTimer, //可等待计时器对象句柄 const LARGE_INTEGER *pDueTime, //计时器第一次触发的时间 LONG lPeriod, //表示计时器每个多长时间触发一次 PTIMERAPCROUTINE pfnCompletionRoutine, LPVOID lpArgToCompletionRoutine, BOOL fResume ); BOOL CancelWaitableTimer( HANDLE hTimer );