windows核心编程-8.用户模式下的线程同步

  1. 原子访问:Interlocked 系列函数
  2. 高速缓存行
  3. 高级线程同步
  4. 关键段
  5. Slim读写锁
  6. 条件变量

8.1 原子访问 Interlocked 系列函数

LONG InterlockedExangeAdd(
    PLONG volatile plAddend,
    LONG lIncrement
);

LONGLONG InterlockedExchangeAdd64(
    PLONGLONG volatile pllAddend,
    LONGLONG llIncrement
);
void * _aligned_malloc(size_t size,size_t alignment)

内存对齐函数

几个赋值的函数

LONG InterlockedExange(
    PLONG volatile plTarget,
    LONG lValue
);//*plTarget=lValue; 返回原来的值即 *plTarget

LONG InterlockedExchange64(
    PLONGLONG volatile plTarget,
    LONGLONG lValue
);

PVOID InterlockedExchangePointer(
    PVOID * volatile ppvTarget,
    PVOID pvValue
);//*ppvTarget=pvValue; 返回原来的值即 *plTarget

几个用来比较然后赋值的函数

LONG InterlockedCompareExchange(
    PLONG plDestination,
    LONG lExchange,
    LONG lComparand
);

LONG InterlockedCompareExchangePointer(
    PVOID* ppvDestination,
    PVOID pvExchange,
    PVOID pvComparand
);

// InterlockedCompareExchange 的伪代码如下
LONG InterlockedCompareExchange(PLONG plDestination,
    LONG lExchange ,Long lComparand){
    LONG lRet=*plDestination;
    if(*plDestination == lComparand )
        *plDestination=lExchange;
    return(lRet);
}
// end

//for x64
LONGLONG InterlockedCompareExchange64(
    LONGLONG pllDestination,
    LONGLONG llExchange,
    LONGLONG llComparand);

其他两个函数

LONG InterlockedIncrement(PLONG plAddend);

LONG InterlockedDecrement(PLONG plAddend);

//others for and , or ,xor
LONGLONG InterlockedAnd64(
    LONGLONG* Destination,
    LONGLONG Value){

    LONGLONG Old;
    do{
        Old=*Destination;
    }while(InterlockedCompareExchange64(Destination,Old & Value,Old)!=Old);
    return Old;
}

从Windows XP 开始,还提供了一系列能对单向链表提供的函数。


8.2 高速缓存行

一个糟糕的设计

struct CUSTINFO{
    DWORD dwCustomerID; // mostly read only
    int nBalanceDue; // read write
    wchar_t szName[100]; // mostly read only
    FILETIME ftLastOrderDate; // read write 
}

这个例子没有考虑到cpu的高速缓存行

//一个简单的改进版
#define CHCHE_ALIGN 64
struct __declspec(align(CACHE_ALIGN)) CUSTINFO{
    DWORD  dwCustomerID ;//most read-only
    wchar_t szName[100]; //most read-only

    __declspec(align(CACHE_ALIGN))
    int nBalanceDue; // r w
    FILETIME ftLastOrderDate; // r w
};

8.3 高级线程同步

如果线程想要访问一个共享资源或者想要得到一些特殊事件的通知时,线程必须调用操作系统的一个函数,并将线程正在等待的东西作为参数传入。
如果OS检测到资源已经可供使用,或者特殊事件已经发生,那么这个函数会立刻返回。这样线程将仍然保持可调度状态。

* 当线程在等待的时候,系统会充当它的代理,系统会记住它想要的资源,并在合适的时候唤醒他
我们要避免两个线程使用共享变量的方式来实现同步。


8.4 关键段

const int COUNT=10;
int g_nSum=0;
CRITICAL_SECTION g_cs;

DWORD WINAPI FirstThread(PVOID pvParam){
    EnterCriticalSection(&g_cs);/////////// enter 
    g_sum=0;
    for(int n=1;n<=COUNT;n++){
        g_nSum+=n;
    }
    LeaveCriticalSection(&g_cs);/////////////// leave
    return(g_nSum);
}

DWORD WINAPI SecondThread(PVOID pvParam){
    EnterCriticalSection(&g_cs);
    g_sum=0;
    for(int n=1;n<=COUNT;n++){
        g_nSum+=n;
    }
    LeaveCriticalSection(&g_cs);
    return(g_nSum);
}
8.4.1 关键段:细节
VOID InitIalizeCriticalSection(PCRITICAl_SECTION pcs);
VOID EnterCriticalSection(PCRITICAL_SECTION pcs);
VOID DeleteCriticalSection(PCRITICAL_SECTION pcs);

VOID TryEnterCriticalSection(PCRITICAL_SECTION pcs);
//如果返回true,那么CRITICAL_SECTION的成员变量已经更新过了,表示该线程正在访问资源
//如果false,线程可以去做别的事情,而不会等待。
8.4.2 关键段和旋转锁
BOOL InitializeCriticalSectionAndSpinCount(
    PCRITICAL_SECTION pcs,
    DWORD dwSpinCount//0~0x00FFFFFFF
);//init

DWORD SetCriticalSectionSpinCount(
    PCRITICAL_SECTION pcs;
    DWORD dwSpinCount;
);//set 

//dwSpinCount 推荐 4000
8.4.3 关键段和错误处理

InitIalizeCriticalSection 函数可能会由于内存不足返回失败,ms最初设计的时候没有考虑到这个问题,所以该函数返回类型为VOID , 返回失败的时候回抛出 STATUS_NO_MEMORY异常
可以使用InitializeCriticalSectionAndSpinCount函数来解决这个问题,内存分配失败的时候返回FALSE

第一次使用EnterCriticalSection创建事件内核对象。
DeleteCriticalSection可以删除事件内核对象。

XP之前,内存不足可能会发生关键段争夺现象,EnterCriticalSection函数会抛出EXCEPTION_INVALID_HANDLE异常。
处理方式:
1. 结构化异常捕获
2. InitializeCriticalSectionAndSpinCount来创建关键段,将dwSpinCount参数的最高位设为1,当函数看到最高位为1的时候,会在初始化的时候创建一个与关键段相关联的时间内核对象。如果无法创建会返回FALSE

XP开始引入了新的有键事件类型的内核对象。


8.5 Slim 读写锁

SRWLock和关键段相同,对一个资源进行保护,不让其他线程访问它。
但SRWLock允许我们区分那些想要读取资源的值的线程和想要更新资源的值的线程、
让所有读取者线程在同一时刻访问资源是可行的,但当写入这线程想要对资源进行更新的时候需要进行同步,这种情况下写入这线程应该独占资源的访问权。

VOID InitializeSRWLock(PSRWLOCK SRWLock);

//for writer
VOID AcquireSRWLockExclusive(PSRWLOCK SRWLock);
VOID ReleaseSRWLockExclusive(PSRWLOCK SRWLock);

//for reader
VOID AcquireSRWLockShared(PSRWLOCK SRWLock);
VOID ReleaseSRWLockShared(PSWRLOCK SRWLock);

8.6 条件变量

有时我们想让线程以原子方式把锁释放并将自己阻塞,直到某个条件成立为止。
windows提供两个函数

BOOL SleepCondtionVariableCS(
    PCONDITION_VARIABLE pConditionVariable,
    PCONDITION_VARIABLE pCriticalSection,
    DWORD dwMilliseconds
);

BOOL SleepCondtionVariableSRW(
    PCONDITION_VARIABLE pConditionVariable,
    PSWLOCK pSRwLock,
    DWORD dwMilliseconds,
    ULONG Flags
);
// pConditionVariable 指向我们的条件变量,
// pCriticalSection  pSRwLock 指向我们的锁变量
VOID WakeConditionVariable(
    PCONDITION_VARIABLE ConditionVariable
);//唤醒一个处于SleepConditionVariable*函数中等待同一个条件变量的线程获得锁。

VOID WakeAllConditionVariable(
    PCONDITION_VARIABLE  ConditionVariable
);//唤醒一个或多个处于SleepConditionVariable*函数中等待同一个条件变量的线程获得锁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值