线程同步——用户模式下的线程同步

1.原子访问:Interlocked系列函数

LONG InterlockedExchangeAdd(
    PLONG volatile plAddend,
    LONG lIncrement);
LONGLONG InterlockedExchangedAdd64(
    PLONGLONG volatile pllAddend,
    LONGLONG llIncrement);

以上两个函数只要传一个长整形变量的地址和另一个增量值,函数就能保证递增操作是以原子方式进行的。
我们必须确保传给这些函数的变量地址是经过对齐的,否则这些函数可能会失败。
C运行库提供了一个_aligned_malloc函数,可以用这个函数来分配一块对齐过的内存。
其他原子操作函数还有:

1. InterlockedIncrement             //给一个值加1
2. InterlockedExchange              //值交换
3. InterlockedExchange64            //值交换
4. InterlockedExchangePointer       //值交换

可以利用以上函数实现旋转锁,旋转锁的缺点是会占用CPU时间,试用于对资源占用时间短的情况。

PVOID InterlockedCompareExchange(
    PLONG plDestination,
    LONG lExchange,
    LONG lComparand);
PVOID InterlockedCompareExchangePointer(
    PVOID* ppvDestination,
    PVOID pvExchange,
    PVOID pvComparand);

这两个函数以原子的方式执行一个测试和设置操作。具体参见P202

2.关键段

首先定义一个名为g_cs的CRITICAL_SECTION数据结构,然后把任何需要访问的共享资源的代码放在EnterCriticalSection和LeaveCriticalSection之间

CRITICAL_SECTION g_cs;
EnterCriticalSection(&g_cs);
//...other code
LeaveCriticalSection(&g_cs);

任何线程试图访问被保护的资源之前都要调用一下函数对CRITICAL_SECTION 数据结构进行初始化

VOID InitializeCriticalSection(PCRITICAL_SECTION pcs);

当线程将不再需要访问共享资源的时候,应该调用下面的函数来清理CRITICAL_SECTION 数据结构

VOID DeleteCriticalSection(PCRITICAL_SECTION pcs);

也可以使用下面的函数来代替EnterCriticalSection,这个函数不会让线程进入等待状态,它会通过返回值来表示是否获准访问资源,资源正在被其他线程访问返回false,其它情况返回true。通过这个函数线程可以快速检测是否可以访问资源,如果不能可以去做些其他事情,而不用等待。

VOID TryEnterCriticalSection(PCRITICAL_SECTION pcs);

关键段和旋转锁
为了提高关键段的性能,Microsoft把旋转锁合并到关键段中。因此,当调用EnterCriticalSection的时候,它会用一个旋转锁不断的循环,尝试在一段时间内获得对资源的访问权。只有当尝试失败的时候,线程才会切换到内核模式并进入等待状态。因为切换内核模式的开销很大(大约1000个CPU时间)。
为了在使用关键段的时候同时使用关键所,必须使用下面的函数来初始化关键段:

BOOL InitializeCriticalSectionAndSpinCount(
    PCRITICAL_SECTION pcs,
    DWORD dwSpinCount);

第一个参数是关键段结构的地址。第二个参数是我们希望旋转锁循环的次数。
作者认为应该在使用关键段的同时总是使用旋转锁,难点在于循环次数的确定。用来保护进程堆的关键段所使用的旋转次数大约是4000,这可以作为我们的一个参考值。

3.Slim读/写锁

SRWLock的目的和关键段相同,不同的是SRWLock可以区分读写操作,多个线程可以对一个资源同时进行读操作,而当写操作的时候则需要进行资源保护。
使用方法:首先需要分配一个SRWLOCK结构并初始化:

VOID InitializeSRWLock(PSRWLOCK SRWLock);

初始化完成后调用以下函数获得对资源的独占访问权:

VOID AcquireSRWLockExclusive(PSRWLOCK SRWLock);

调用以下函数释放对资源的锁定:

VOID ReleaseSRWLockExclusive(PSRWLOCK SRWLock);

对于读取者线程则调用以下两个函数来操作资源:

VOID AcquireSRWLockShared(PSRWLOCK SRWLock);
VOID ReleaseSRWLockShared(PSRWLOCK SRWLock);

不需要删除或者销毁SRWLOCK结构,系统会自动清理。

总结一下,如果希望在应用程序中得到最佳性能,那么首先应该尝试不要共享数据,然后依次使用volatile读取,volatile写入,Interlocked API,SRWLock以及关键段。当且仅当所有这些都不满足要求的时候,再使用内核对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值