效率
you should try to use nonshared data first and then use volatile reads, volatile writes, interlocked APIs, SRWLocks, critical sections.
And if all of these won't work for your situation, then and only then, use kernel objects
InterlockedExchange系列函数是针对于某个单一变量进行的原子操作
Volatile关键字告诉编译器不要对变量的访问做任何优化
运行的速度极快:调用一个互锁函数通常会导致执行几个C P U周期(通常小于5 0),并且不会从用户方式转换为内核方式(通常这需要执行1 0 0 0个C P U周期)。
允许多个读线程同时读取共享数据,但在同一时间只允许一个写线程修改共享数据。
用法:
写者:
读者:
我们并不需要销毁读写锁对象,因为系统自动处理读写锁对象。
读写锁的效率比临界区要高到少一倍左右。
临界区对象用法:
不能跨进程同步,请使用互斥内核对象。
1. 每个共享资源使用一个CRITICAL_SECTION变量;
2. 如果一个线程中同时进入了2个或更多的关键代码段,则其他同类线程的进入顺序必须相同,否则可能产生死锁;
3. InitializeCriticalSectionAndSpinCount作用:多处理器上可能有利于效率;有返回值(初始化分配内存可能失败);设置dwSpinCount 的高位,则会预先分配内核对象(事件)的空间——某种程度上避免的EnterCriticalSection函数失败;
4. 如果不想进程暂停那么试试TryEnterCriticalSection;
5. 实际上,EnterCriticalSection也会超时,通常是30天左右.
条件变量要与临界区对象或读写锁对象一起使用。
SleepConditionVariableCS、
WakeConditionVariable、
WaitForSingleObject WaitForMultipleObjects
MsgWaitForMultipleObjects
CreateEvent CreateEventEx SetEvent ResetEvent OpenEvent
CreateSemaphore OpenSemaphore
CreateMutex OpenMutex ReleaseMutex
DWORD WaitForSingleObject(HANDLE hObject,DWORD dwMilliseconds);
DWORD WaitForMultipleObjects(DWORD dwCount,
CONST HANDLE* phObjects,
BOOL fWaitAll,
DWORD dwMilliseconds);
返回值:WAIT_FAILED,WAIT_TIMEOUT,WAIT_OBJECT_0+X
MsgWaitForMultipleObjects函数
这个函数是用来在GUI现成中等待对象被激发,是用来修改主消息循环的
这个函数非常像WaitForMultipleObjects(),但是他会在“对象被激发”
或“消息到达队列”时被唤醒而返回。MsgWaitForMultipleObject()函数
比WaitForMultipleObject()多接受一个参数,允许指定哪些消息是观察对象
MsgWaitForMultipleObject()的正确使用方式是改写主消息循环,使得激发状态
的HANDLE得以像消息一样被等待
BOOL fManualReset,
自动事件:
手动事件:
如果是AutoResetEvent,那么在signed之后,WaitForXXObject返回之前,会再次被unsigned.
1) 多个线程WaitForSingleObject同一个AutoResetEvent,那么此对象signed之后,哪个线程变为可调度呢?(算法是公平的);signed之后,WaitForXXObject返回之前,会再次被unsigned
2) 进程句柄、线程句柄、事件等等内核对象,正常使用时(运行时)都是unsigned的,完蛋了才signed;
3) 完蛋了的Object才能使WaitForSingleObject或WaitForMultipleObjects返回(当然超时或出错除外);
4) WaitForMultipleObjects如果不是WaitAll,那么句柄数组中有一个signed的就会返回,返回值 dwResult - WAIT_OBJECT_0 表示signed的句柄的索引值;
5) ManualResetEvent和AutoResetEvent的差别在于:AutoResetEvent可以自动调度所有需要读写同一数据的线程,弊端是:无法确定谁先被调度;
等待定时器与用户定时器(用SetTimer函数进行设置)之间的最大差别是: 用户定时器需要在应用程序中设置许多附加的用户界面结构,这使定时器变得资源更加密集。另外,等待定时器属于内核对象,这意味着它们可以供多个线程共享,并且是安全的。
PSECURITY_ATTRIBUTE psa,
LONG lInitialCount, //
当
WaitForSingleObject会使
假设信标中有3可用资源,并且有5个线程在等待,那么就会有3个线程变为可调度,同时资源数减为0.那么还有2个线程仍在等待。此时,有个xx线程(或xx函数)准备好了一个可用资源,打算让等待中的线程处理该资源,那么此xx可以调用 ReleaseSemaphore(hSemaphore, 1, NULL); 来添加一个资源数。在这之后,等待中的一个线程立刻获得调度权限,信标内资源数再次减为0,剩余最后一个线程继续苦苦等待...
注意事项:ReleaseSemaphore的第二个参数传入0将毫无效果,第三个参数也不会正确地返回原来的资源数量;
PSECURITY_ATTRIBUTES psa,
BOOL fInitialOwner,
PCTSTR pszName);
互斥对象的特别之处:
互斥对象的结构中有一个当前归属的线程ID成员,和使用计数成员。当该对象归属于某一个线程(A)时(即WaitForXXObject成功),那么dwID就设置为调度线程A的ID,同时dwCount设置为1。如果该线程再次WaitForXX,那么函数会立即返回,因为是同一dwID嘛,但是dwCount会递增1。如果此时另外一个线程B也来凑热闹,那么会进入睡眠状态,直到A调用ReleaseMutex相应的次数,那么dwID将设置为B的id,且dwCount为1。也就是说只要dwID不为0,那么该对象绝对是unsigned的;如果为0,那么该对象必定是signed的。
当一个线程拥有了互斥对象,但是却意外终止了(例如TerminateThread --),没有来得及调用ReleaseMutex,那么系统将视为该对象已经被释放,其他等待的线程之一将获得调度,只不过WaitForxx返回值是WAIT_ABANDONED,表明上一个线程是非正常中止的,你(此线程)看着办吧。
在Windows中使用互斥锁可以有效的避免死锁。当持有Mutex的线程在调用ReleaseMutex前意外退出,如ExitThread和TerminateThread。系统会把Mutex分给等待Mutex的线程中的一个并返回Wait_Abandoned,从而避免了死锁
1.WaitForInputIdle:传递一个进程句柄给它,那么就可以等待直到该进程创建完毕,显示了窗口,并且等待用户进行输入——这太有用了,当你的程序创建了一个子进程的时候,很可能需要知道子进程何时完全创建完毕,有了它,一切变得那么简单!
2.MsgWaitForMultipleObjects(Ex): 这两个函数可以用在同时处理消息的线程中,这样当句柄们都signed或者来了消息时都可以返回.具体和时返回取决于dwWakeMask参数~;
3.WaitForDebugEvent:等待调试事件,估计只有开发调试器时才可能用得到吧;
4.SignalObjectAndWait:该函数允许同时执行2件事:第一件事是把hObjectToSignal 置为signed,第二件事是等待hObjectToWaitOn。