2024年4月2日发(作者:)

使用Win32提供的临界区可以方便的实现线程锁:

// 全局:

CRITICAL_SECTION cs;

InitializeCriticalSection( & cs);

// 线程1:

EnterCriticalSection( & cs);

int a = s.a;

int b = s.b;

LeaveCriticalSection( & cs);

// 线程2:

EnterCriticalSection( & cs);

s.a ++ ;

s.b -- ;

LeaveCriticalSection( & cs);

// 最后:

DeleteCriticalSection( & cs);

代码中的临界区变量(cs)就可以看作是变量s的锁,当函数

EnterCriticalSection返回时,当前线程就获得了这把锁,之后就是对变量的

访问了。访问完成后,调用LeaveCriticalSection表示释放这把锁,允许其他

线程继续使用它。

如果每当需要对一个变量进行加锁时都需要做这些操作,显得有些麻烦,而

且变量cs与s只有逻辑上的锁关系,在语法上没有什么联系,这对于锁的管理

带来了不小的麻烦。程序员总是最懒的,可以想出各种偷懒的办法来解决问题,

例如让被锁的变量与加锁的变量形成物理上的联系,使得锁变量成为被锁变量不

可分割的一部分,这听起来是个好主意。

首先想到的是把锁封闭在一个类里,让类的构造函数和析构函数来管理对锁

的初始化和锁毁动作,我们称这个锁为“实例锁”:

class InstanceLockBase

... {

CRITICAL_SECTION cs;

protected :

InstanceLockBase() ... { InitialCriticalSection( & cs); }

~ InstanceLockBase() ... { DeleteCriticalSection( & cs); }

} ;

如果熟悉C++,看到这里一定知道后面我要干什么了,对了,就是继承,因

为我把构造函数和析构函数都声明为保护的(protected),这样唯一的作用就

是在子类里使用它。让我们的被保护数据从这个类继承,那么它们不就不可分割

了吗:

struct MyStruct: public InstanceLockBase

... { „ } ;

什么?结构体还能从类继承?当然,C++中结构体和类除了成员的默认访问

控制不同外没有什么不一样,class能做的struct也能做。此外,也许你还会

问,如果被锁的是个简单类型,不能继承怎么办,那么要么用一个类对这个简单

类型进行封装(记得Java里有int和Integer吗),要么只好手工管理它们的

联系了。如果被锁类已经有了基类呢?没关系,C++是允许多继承的,多一个基

类也没什么。

现在我们的数据里面已经包含一把锁了,之后就是要添加加锁和解锁的动

作,把它们作为InstanceLockBase类的成员函数再合适不过了:

class InstanceLockBase

... {

CRITICAL_SECTION cs;

void Lock() ... { EnterCriticalSection( & cs); }

void Unlock() ... { LeaveCriticalSection( & cs); }

} ;

看到这里可能会发现,我把Lock和Unlock函数都声明为私有了,那么如何

访问这两个函数呢?是的,我们总是需要有一个地方来调用这两个函数以实现加

锁和解锁的,而且它们总应该成对出现,但C++语法本身没能限制我们必须成对

的调用两个函数,如果加完锁忘了解,那后果是严重的。这里有一个例外,就是

C++对于构造函数和析构函数的调用是自动成对的,对了,那就把对Lock和

Unlock的调用专门写在一个类的构造函数和析构函数中:

class InstanceLock

... {

InstanceLockBase * _pObj;

public :

InstanceLock(InstanceLockBase * pObj)

... {

_pObj = pObj; // 这里会保存一份指向s的指针,用于解锁

if (NULL != _pObj)

_pObj -> Lock(); // 这里加锁

}

~ InstanceLock()