l 用户模式下的线程同步:
Volatile类型:告诉编译器必须到内存取变量值,而不使用寄存器中的值。因为每个线程都有各自的寄存器,它们的寄存器中同一变量的值可能不一致。(例如嵌入汇编代码,编译器无法觉察到变量已经被修改了)
原子访问:指的是一个线程在访问某个资源的时候,能够保证没有其他线程访问该资源。
相关函数:
InterlockedExchangeAdd(…):原子加减法(传负值表示做减法);
InterlockedIncrement(…):原子加法;
InterlockedExchange(…):原子替换;
InterlockedCompareExchange(…)。
(
原理:Interlocked函数会在总线上维持一个硬件信号,该信号阻止其他CPU访问同一个内存地址。
原子锁的速度很快。
一般在多CPU的机器上比较有用,在单CPU机器上不使用原子锁。
)
关键段:
(1) 定义一个CRITICAL_SECTION结构的变量g_cs;
(2) 调用InitialsizeCriticalSection(&g_cs)初始化;
(3) 把需要共享的代码放在EnterCriticalSection( &g_cs )和LeaveCriticalSection( &g_cs )之间(还有TryEnterCriticalSection函数);
(4) 调用DeleteCriticalSection(&g_cs)释放;
(当已有一个线程访问被锁定的资源,另一个线程想要访问,必须进入等待状态)
(5) 旋转锁:可以在使用关键段的同时使用旋转锁,以提高效率。(相关函数:InitalizeCriticalSectionAndSpinCount()、SetCriticalSectionSpinCount())。
(为什么加旋转锁能够提高效率?
因为当一个线程试图进入一个关键段,而该关键段正被另一个线程占用,此时该线程会切换到等待状态。也就是说需要从用户模式切换到内核模式,这样会影响关键段的效率。加了旋转锁,线程就会再一段时间不断尝试进入关键段,只有多次尝试失败,才会进入等待状态,这样就减少了经常切换到内核模式,从而提高效率。)
Slim读/写锁:
(1) 初始化:InitializeSRWLock();
(2) 读锁:AcquireSRWLockExclusive()、ReleaseSRWLockExclusive();
(3) 写锁:AcquireSRWLockShared()、ReleaseSRWLockShared()。
(读锁和写锁的区别:读锁允许多个线程同时读取一块数据,因为仅仅读取数据并不会更改数据,因此不需要考虑同步的问题;写锁要求独占对资源的访问权,因此,在一个线程写入数据时,其他线程不允许对数据进行读或写。)
效率先后:volatile、原子访问、读写锁、关键段、内核对象。
(
volatile:非常快;
原子访问:CPU需要锁定内存,因此比volatile慢;
关键段:比较慢,因为包含进入和离开两个操作,并且这两个操作需要修改CRITICAL_SECTION结构中多个字段;
读写锁:读比写快,因为读数据可以多个线程同时进行,写数据必须独占进行;
内核对象(如:互斥量):比较慢,总是需要在用户模式和内核模式之间切换。
)
l 用内核对象进行线程同步:
内核对象的状态:对线程同步来说,每一种内核对象要么处于触发状态,要么处于非触发状态(例如,进程内核对象在创建的时候处于非触发状态,在终止的时候变成触发状态)。
等待函数:
WaitForSingleObject():等待单个对象触发;
WaitFoMultipleObjects():等待多个对象触发;
事件内核对象实现线程同步:
(1)创建事件内核对象:
可通过CreateEvent()或CreateEventEx()函数创建事件内核对象,被创建的事件可以是自动重置事件或手动重置事件。
(2)事件内核对象同步:
在不同线程中,可通过DuplicateHandle()或OpenEvent()来访问已经创建的事件内核对象。
(3)重置事件内核对象:
SetEvent():将事件变成触发状态;
ResetEvent():将事件变成非触发状态。
可等待的计时器内核对象:
它可以在某个指定的时间触发,或每隔一段时间触发一次。
(1)相关函数:
CreateWaitableTimer():创建计时器;
OpenWaitableTimer():打开已经存在的计时器;
SetWaitableTimer():设置计时器;
CancleWaitableTimer():撤销计时器;
(2)和用户计时器(OnTimer)比较:
1.用户计时器需要大量的用户界面基础设施,从而消耗更多资源;
2.可等待计时器是内核对象,可在多个线程之间共享,可具备安全性;而用户计时器触发时,只有一个线程得到通知;
3. WM_TIMER消息的优先级是最低的,只有当线程消息队列中没有其他消息的时候才会被处理。
信号量内核对象:
信号量内核对象用来对资源进行计数,包含两个参数:一个最大资源计数和一个当前资源计数。
互斥量:
互斥量内核对象用来确保一个线程独占对一个资源的访问,它的优势在于能在不同的进程间共享。