用户方式同步具有速度快的优点。但是它的缺点特是显而易见的。
- 首先,用互锁函数家族的时候,这些函数只能用在单值数据上
- 如果用关键代码段的话,只能对单个进程中的线程进行同步。而且容易出现死琐,而且不能设置等待时间
那么内核对象就克服了用户方式同步的几个缺点,能够在不同进程间对线程实施同步,而且处理完全不止在单值数据上。但是内核方式同步线程时,必须将线程进入到内核方式,这样就使处理速度大大降低。这也是内核方式的缺点,所以还是能用用户方式处理的就用用户方式来处理。
现在来说下一个很多内核对象都具有的特性,这就是
signaled/nonsignaled
特性。
这个特性是这样的:在内核对象处于运行状态时,他就是
nonsignaled
状态,当内核对象运行结束时,就会处于
signaled
状态。在内核对象中是用一个
bool
值来表示的。所以查看内核对象的状态很容易。只要查这个值就可以了。所以当线程要等待的内核对象处于
signaled
状态时,那么这个线程就可以被
CPU
调度,否则处于等待状态。
拥有这个特性的内核对象有:
·
Processes
|
·
File change notifications
|
·
Threads
|
·
Events
|
·
Jobs
|
·
Waitable timers
|
·
Files
|
·
Semaphores
|
·
Console input
|
·
Mutexes
|
现在就来重点说下
Event,waitable timer,Semaphore,Mutex.
这四个用的比较多。
在说这四个内核对象之前先来说下能让线程进入等待状态的函数,也就是去等待上面四个内核对象的状态的函数。他们有
DWORD WaitForSingleObject(HANDLE hObject,DWORD dwMillisecounds)
参数:
- 第1个参数就是要等待的对象的HANDLE,可以是上面四个中的一个。
- 第2个参数是等待超时时间。
返回值
- WAIT_OBJECT_0//成功等待。也就是内核对象已经为signaled,线程成为可调度线程
- WAIT_TIMEOUT//因为超时,线程重新唤醒,成为可调度的
- WAIT_FAILED//将一个错误的值传递给WaitForSingleObject,比如传递了一个无效句柄。
DWORD WaitForMultipleObjects(DWORD dwCount,CONST HANDLE *phObjects,BOOL fWaitAll,DWORD dwMillisecounds)
参数:
- dwCount //想要让函数查看的内核对象的数目,这个值必须在1到MAXIMUN_WAIT_OBJECTS(64)
- phObjects //指向内核对象句柄的数组的指针
- fWaitAll //如果为TRUE,就是等待到所有内核对象都signaled。FALSE则是只要有一个signaled就唤醒
- dwMillisecounds //超时时间
返回值:
和
WaitForSingleObject
一样。只是,如果
fWaitAll==false
。那么如果想得到哪个对象
signaled
就可以将返回值剪去
WAIT_OBJECT_0
。(前提是,返回值即不是
WAIT_TIMEOUT
也不是
WAIT_FAILED
)
其实再调用上面两个函数时,如果等待成功(就是返回值为
WAIT_OBJECT_0
),会对内核对象产生一个副作用。有的内核对象的副作用就是改变的内核对象的状态(比如自动清楚内核对象)。有的内核对象则没有任何副作用(比如线程,进程内核对象)。
下面就来逐一介绍
Event,Waitable timer,Semaphrone,Mutex
对象
先是
Event
对象。
Event
对象是最基本的内核对象。它包括:
1.
使用计数器
2.
自动重置
/
人工重置开关
3.
signaled/nonsignal
开关
1
,
3
不用多介绍。
重点说下
2
。什么叫自动重置?就是所有等待的线程,当
Event
对象变成
signaled
状态时,只会随即的挑选一个线程进入可调度状态。然后
Event
对象会被自动置为
nonsignaled
状态。而人工重置是当
Event
对象变成
signaled
时候,所有等待的线程都会被置为可调度状态。
CPU
会给他们都分配时间片。而且不会自动将
Event
状态置为
nonsignaled
。要通过
ResetEvent
将
Event
变为
nonsignaled
。
至于创建
Event
内核对象
CreateEvent
函数,可以查找
MSDN
。
然后就是
WaitableTimer
内核对象。
CreateWaitableTimer OpenWaitableTimer
的使用可以查找
MSDN
。重点说下设置
WaitableTimer
设置
WaitableTimer
使用
SetWaitbaleTimer
。原形如下:
BOOL SetWaitableTimer(
HANDLE hTimer, // handle to a timer object
const LARGE_INTEGER *pDueTime, // when timer will become signaled
LONG lPeriod, // periodic timer interval
PTIMERAPCROUTINE pfnCompletionRoutine, // pointer to the completion routine
LPVOID lpArgToCompletionRoutine, // data passed to the completion routine
BOOL fResume // flag for resume state
);
const LARGE_INTEGER *pDueTime, // when timer will become signaled
LONG lPeriod, // periodic timer interval
PTIMERAPCROUTINE pfnCompletionRoutine, // pointer to the completion routine
LPVOID lpArgToCompletionRoutine, // data passed to the completion routine
BOOL fResume // flag for resume state
);
参数:
- *pDueTime是等待定时器第1次报时的时间。可以为这个参数传递0,表示调用函数的时候就报时。或者传递一个负值,表示调用这个函数开始延迟n*100ns后开始第1次报时。
- pfnCompletionRoutine指向一个函数的地址。如果这项不为NULL,那么当定时器报时的时候,会讲这个函数放入线程的APC队列。(什么是APC会在以后详细介绍)
下面再来说下
Semaphore
。
Semaphroe
的作用是为一个资源计数,而这个资源又是多个的。比如说你想对一个拥有
10
个元素的数组进行同步。那么就可以用
Semaphore
。当
10
个元素都被占用的时候。
Semaphore
就处于
nonsignaled
状态,反之,有元素可以被处理的时候就处于
nignaled
状态。
Semapore
包括:
1.
使用计数器
2.
最大资源数量
3.
当前可使用资源数量
他的使用规则是:
1.
当前资源大于
0
,发出信标信号(处于
signaled
状态)
2.
当前资源等于
0
,不发出信号
3.
0<=
当前资源
<=
最大资源
CreateSemaphore OpenSemaphore ReleaseSemaphore
可以查看
MSDN
ReleaseSemaphore
是将
Semaphore
的可使用资源进行增加。
最后就是
Mutex
了。
Mutex
是使用的最多的线程同步内核对象。它的作用是保证线程对单个资源的互斥访问权。它包括:
- 使用计数器
- 拥有Mutex的线程ID
- 递归计数器//该线程拥有互斥对象的次数
Mutex
的使用规则是:
- 线程ID为0,互斥对象不被任何线程所拥有,并且发出该互斥对象的通知信号
- 线程ID不为0,那么线程就拥有该互斥对象,不发出通知信号
CreateMutx OpenMutex ReleaseMutex
查看
MSDN
下面是互斥对象与关键代码段的比较
Characteristic
|
Mutex
|
Critical Section
|
Performance
|
Slow
|
Fast
|
Can be used across process boundaries
|
Yes
|
No
|
Declaration
|
HANDLE hmtx;
|
CRITICAL_SECTION cs;
|
Initialization
|
hmtx= CreateMutex (NULL, FALSE, NULL);
|
InitializeCriticalSection(&cs);
|
Cleanup
|
CloseHandle(hmtx);
|
DeleteCriticalSection(&cs);
|
Infinite wait
|
WaitForSingleObject (hmtx, INFINITE);
|
EnterCriticalSection(&cs);
|
0 wait
|
WaitForSingleObject (hmtx, 0);
|
TryEnterCriticalSection(&cs);
|
Arbitrary wait
|
WaitForSingleObject (hmtx, dwMilliseconds);
|
Not possible
|
Release
|
ReleaseMutex(hmtx);
|
LeaveCriticalSection(&cs);
|
Can be waited on with other kernel objects
|
Yes (use WaitForMultipleObjects or similar function)
|
No
|