您的位置:首页 > 编程语言

读书笔记:Windows核心编程第八章---用户方式中的线程同步技巧

2013-04-14 12:40 363 查看
                                                Windows核心编程第八章  用户方式中的线程同步技巧

线程在以下两种情况下需要互相通信:

1、当有多个线程访问共享资源而不使资源被破坏的时候;

2、当一个线程需要将某个任务完成的情况通知另一个或多个线程时候。

 

一、原子访问:互锁的函数家族

原子访问是指线程在访问资源时候能够确保所有的其他线程都不在同一时间内访问相同的资源。

 

以下函数会把第二个参数的值以原子操作的方式加到第一个参数指向的指上:

LONG  InterlockedExchangeAdd(

         PLONG  plAdded,

         LONG  Increment);

 

LONGLONG  InterlockedExchangeAdd64(

         PLONG  plAdded,

         LONG  Increment);

他执行的极快一般只需要小于50个CPU周期;也不需要在用户模式和内核模式中切换(这个一般需要1000个以上的CPU周期)。

 

以下三个函数会把第一个参数指向的内存地址替换为第二个参数指定的值:

         LONG InterlockedExchange(

                   PLONG volatile plTarget,

                   LONG  lvalue);

         LONGLONG InterlockedExchange64(

                   PLONGLONG volatile plTarget,

                   LONGLONG  lvalue);

PVOID  InterlockedExchangePointer(

                   PVOID* volatile pplTarget,

                   PVOID  pvlvalue);

这个函数可以用于实现循环锁,循环锁在循环的时候会浪费CPU时间,我们可以使用Sleep()或者使用SwitchRiThread(),同时我们必须确保循环锁变量和锁保护的数据位于不同的高速缓存行,一面使用资源的CPU和任何试图访问资源的CPU发生争夺,影响性能。。。

 

下面是最后两个InterLocked交换函数,他们将参数一和参数三进行比较如果参数一等于参数三那么就吧参数而赋值给参数一:

PLONG InterlockedCompareExchange(

         PLONG  plDestination,

         LONG  lExchange,

         LONG  lComparand);

PLONG InterlockedCompareExchange(

         PVOID*  ppvlDestination,

         PVOID  pvlExchange,

         PVOID  pvlComparand);

 

二、高速缓存行

当CPU从内存里读取一个字节的时候,他并不是从内存中取回一个字节,而是取回一个高速缓存行,当一个CPU改变自己的高速缓存行的时候他会通知其他的CPU使自己的高速缓存行作废。。。所以我们必须根据缓存行的大小将应用程序的数据组织到一起,并且将数据缓存行边界对齐。遮掩做的目的是为了确保不同的CPU能够各自访问不同的内存地址,而且这些地址不在一个高速缓存行中。

我们使用GetLogicalProcessorInformation函数,他会返回一个SYSTEM_LOGICAL_PROCESSOR_INFORMATION结构,检查其中的Cache字段里面的LineSize字段表示高速缓存行大小。遮掩我们就可以使用C/C++编译器的__declspec(align(#))来对字段对齐加以控制。。。

 

三、高级线程同步

通用的规则,我不既不应该使用旋转锁也不应该进行轮询,而应该调用函数把线程切换到等待状态,知道县城想要访问的资源可供使用为止。。。使用volatile关键字防止编译器对代码做某些不必要的优化。。。。

 

四、关键段

关键段函数:

VOID  InitializeCriticalSection(PCRITICAL_SECTION pcs);

VOID  DeleteCriticalSection(PCRITICAL_SECTION pcs);

VOID  EnterCriticalSection(PCRITICAL_SECTION pcs);

VOID  LeaveCriticalSection(PCRITICAL_SECTION pcs);

BOOL  TryEnterCriticalSection(PCRITICAL_SECTION pcs);

关键段和旋转锁一起使用:

BOOL  InitializeCriticalSectionAndSpinCount(

PCRITICAL_SECTION  pcs,

DWORD  dwSpinCount);

DWORD  SetCriticalSectionSpinCount(

PCRITICAL_SECTION  pcs,

DWORD  dwSpinCount);

关键段的错误处理,处理结构化异常。。。

 

五、Slim读写锁

SRWLock他目的和关键段相同,但是他区分读取线程和写入线程、读取线程共享操作、写入线程独占操作:

VOID  InitializeSRWLock(PSRWLOCK SRWLock);

VOID  AcquireSRWLockExclusive(PSRWLOCK SRWLock);

VOID  ReleaseSRWLockExclusive(PSRWLOCK SRWLock);

VOID  AcquireSRWLockShare(PSRWLOCK SRWLock);

VOID  ReleaseSRWLockShare(PSRWLOCK SRWLock);

 

六、条件变量

有时候我们想让线程以原子方式把锁释放并将自己阻塞,知道某个条件成立为止:

         BOOL SleepConditionVariablecs(

                   PCONDITION_VARIABLE  pConditionSection,

                   PCRITICAL_SECTION  pCriticalSection,

DWOED  dwMilliseconds);

         BOOL SleepConditionVariableSRW(

                   PCONDITION_VARIABLE  pConditionSection,

                   PSRWLOCK  pSRWLOCK,

DWOED  dwMilliseconds,

ULONG  Flags);

BOOL WakeConditionVariable(

                   PCONDITION_VARIABLE  pConditionSection);

BOOL WakeAllConditionVariable(

                   PCONDITION_VARIABLE  pConditionSection);

 

七、一些有用的窍门和技巧

1、以原子方式操作一组对象时使用一个锁;

2、同时访问多个逻辑资源使用的锁的次序相同;

3、不要长时间使用锁;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息