处于同一进程内的多线程数据同步有一下几种方式:
1、InterLock系列函数
主要用于单个资源的同步
2、旋转锁
单CPU的机器,不要使用自旋锁,会出现死锁的状态。(因为当前线程一直在锁内旋转,单个CPU是没有机会切换到另一个线程中进行执行)
3、volatile关键字
如果没有可用的同步方式时,可以使用volatile进行同步。(这种方式很劣势,当两个线程不在同一优先级上,会出现死锁)
4、关键段(CRITICAL_SECTION)
关键段的初始化和反初始化
void initializeCriticalSection(PCRITICAL_SECTION cs);
void DeleteCriticalSection(PCRITICAL_SECTION cs);
a.阻塞模式的关键段使用:(会让调用者线程进入等待状态)
EnterCriticalSection( cs );
//需要同步的资源
LeaveCriticalSection( cs );
b.非阻塞模式的关键段使用:(不会让调用者线程进入等待状态,可以访问资源就访问,否则立即执行下面的代码)
if( TryEnterCriticalSection(sc) == TRUE) {
//需要同步的资源
LeaveCriticalSection( cs );
}
c. 阻塞模式的关键段会将调用者线程从用户模式切换到内核模式,这种切换的代价很大;如果是多CPU的机器就有点浪费,可能当前占用资源的线程在另一个CPU上运行,而且很快就会结束对资源的访问。这时,我们使用带旋转锁的关键段最后,调用线程如果在制定的旋转次数能还没获取资源的访问权,则线程才进入等待状态。
使用下面的初始化函数来初始化一个带旋转锁的关键段:
BOOL InitializeCriticalSectionAndSpinCount(PCRITICAL_SECTIONcs, DWORDdwSpinCount); 如果在单CPU机器上dwSpinCount会被忽略,总是为0.
更改关键段的旋转次数(参考值4000):
DWORD SetCriticalSectionSpinCount(PCRITICAL_SECTION cs, DWORD dwSpinCount); 跟上面一样,如果单CPU环境,会忽略掉dwSpinCount参数。
5、读/写锁SRWLock
通过读写锁将想要读取资源的线程和更新资源的线程分开。只有在写入者线程更新资源时,才需要同步;此时,写入者线程对资源具有独占权限,读取者线程是无法访问资源。
VOID InitializeSRWLock(PSRWLOCK SRWLock); //初始化读写锁
写锁访问资源;
VOID AcquireSRWLockExclusive(PSRWLOCK SRWLock);
//对资源进行写操作
VOID ReleaseSRWLockExclusive(PSRWLOCK SRWLock);
读锁访问资源:
VOID AcquireSRWLockShared(PSRWLOCK SRWLock);
//对资源进行读访问
VOID ReleaseSRWLockShared(PSRWLOCK SRWLock);
6、条件锁(这是一种高级锁):
案例:如果读取者线程没有数据可以读取,那么它应该将锁释放并等待,直到写入者线程残生了新的数据为止。如果,用来接收写入者线程产生的数据已满,那么写入者线程应该释放掉锁,并进入等待状态,直到读取者线程把数据清空为止。
对于上面的案例,此时使用关键段和读写锁已经不行了;好的是Windows提供条件锁来同步这种操作。线程可以以原子方式把锁释放并将自己阻塞,直到某一条件成立为止。
BOOL SleepConditionVariableCS(...);
BOOL SleepConditionVariableSRW(...);
当另一个线程检测到相应的条件已经满足的时候,比如存在一个元素可让读取者线程读取,或者有足够的空间让写入者线程插入新的线程,它会调用下面的函数。
VOID WakeConditionVariable(PCONDITION_VARIABLE ConditionVariable);
VOID WakeAllConditionVariable(PCONDITION_VARIABLE ConditionVariable);