在以下两种基本情况下,线程之间需要相互通信:
1、需要让多个线程同时访问一个共享资源,同时不能破坏资源的完整性。(互斥)
2、一个线程需要通过通知其他线程某种任务已经完成。(同步)
用户模式下的线程同步
Interlocked系列函数
Interlocked系列函数会以原子方式来操纵一个值,所谓的原子访问,指的是一个线程在访问某个资源的同时能够保证没有其他线程会同一时刻访问同一资源。
使用方式形如:InterlockedExchangeAdd(&g_x,1) ; //以原子方式增加g_x的值。
举例其中几个函数:
//递增操作 LONG InterlockExchangeAdd(PLONG volatile plAddend,LONG lIncrement) ; LONGLONG InterlockedExchangeAdd64(PLONGLONG volatile pllAddend,LONGLONG llIncrement) ; //替换操作 LONG InterlockedExchange(PLONG volatile plTarget,LONG lValue) ; LONGLONG InterlockedExchange64(PLONGLONG volatile plTarget,LONGLONG lvalue) ; PVOID InterlockedExchangePointer(PVOID* volatile ppvTarget,PVOID pvValue) ;
-------------------------------------------------------------------------------------------------------
关键段(临界区)
关键段(critical section)是一小段代码,它在执行之前需要独占对一此共享资源的访问权。这各方式可以让多行代码以“原子方式”来对资源进行操控。
使用CRITICAL_SECTION的时候,只有两个必要条件:
第一个条件是所有想要访问资源的线程必须知道用来保护资源的CRITICAL_SECTION结构的地址。
第二个条件是在任何线程试图访问被保护的资源之前,必须对CRITICAL_SECTION结构的内部成员进行初始化。
PS:一个已经获准访问资源的线程,再次调用EnterCriticalSection时,会累加计算,如果想要让其它线程能够访问该资源,必须让之前获准访问资源的线程,调用相同次数的LeaveCriticalSection。
其中几个关键函数:
VOID InitialzeCritialSection(PCRITICAL_SECTION pcs); //初始化关键段 VOID DeleteCritialSection(PCRITICAL_SECTION pcs) ; //删除关键段 VOID EnterCritialSection(PCRITICAL_SECTION pcs) ; //尝试进入关键段 VOID LeaveCritialSection(PCRITICAL_SECTION pcs) ; //离开关键段
------------------------------------------------------------------------------------------------------
Slim读/写锁
SRWLock的目的和关键段相同:对一个资源进行保护,不让其他线程访问它。但是,与关键段不同的是,SRWLock允许我们区分那些想要读取资源的值的线程(读取者线程)和想要更新的资源的值的线程(写入者线程)。让所有的读取者线程在同一时刻访问共享资源是可行的,而且写入者线程更新资源时,应该独占访问。
几个主要函数:
VOID InitialzeSRWLock(PSRWLOCK SRWLock) ; //初始化读写锁
VOID AcquireSRWLockExclusive(PSRWLOCK SRWLock) ; //写入者线程调用的独占访问
VOID ReleaseSRWLockExclusive(PSRWLOCK SRLock) ;//写入者线程解除对资源的锁定
VOID AcquireSRWLockShared(PSRWLOCK SRWLock) ; //读取者获取资源的访问权
VOID ReleaseSRWLockShared(PSRWLOCK SRWLock) ; //读取者线程解除对资源的锁定
------------------------------------------------------------------------------------------------------
条件变量
如果我们想让线程以原子方式把锁释放并将自己阻塞,直到某一个条件成立为止。此时可采用Windows提供“条件变量”实现线程同步。
几个关键函数:
BOOL SleepConditionVariableCS(
PCONDITION_VARIABLE pConditionVariable,
PCRITICAL_SECTION pCriticalSection,
DWORD dwMilliseconds) ;
BOOL SleepConditionVariableSRW(
PCONDITION_VARIABLE pConditionVariable,
PSRWLOCK pSRWLock,
DWORD dwMilliseconds,
ULONG Flags
) ;
VOID WakeConditionVariable(
PCONDITION_VARIABLE ConditionVariable) ;
VOID WakeAllConditionVariable(
PCONDITION_VARIABLE ConditionVariable) ;
-------------------------------------------------------------------------------------------------------
高速缓存行
如果想为装配有多处理器的机器构建高性能的应用程序,那么应该注意高速缓存行。当CPU从内存中读取一个字节的时候,它并不只是从内存中取回一个字节,而是取回一个高速缓存行。高速缓存行存在的目的是为了提高性能。一般来说,应用程序会对一组相邻的字节进行操作(空间局部性)。如果所有字节都在高速缓存中,那么CPU就不必访问内存总线,后者耗费的时间比前者耗费的时间要多得多。但是,在多处理器环境中,高速缓存线使得对内存的更新变得更加困难。因为,当一个CPU修改了高速缓存行中的一个字节时,机器中的其他CPU会收到通知,并使自己的高速缓存行作废。例如,当一个CPU必须将它的高速缓存写回内在中,另外一个CPU必须重新访问内存来填满它的高速缓存行,这会损伤性能。