锁管理器的实现

锁管理器LockManager用于解决这样的问题:

我们需要一个锁对应一种资源,但是需要上锁的资源的种类可能非常的多,或者甚至是未知的。 这种情况下,我们不可能一次初始化所有的锁。如果不能对这些未知资源上锁的话,就不可能实现对这些资源的互斥访问了。

举个例子,有一张无限的地图,玩家可以在在地图上按照坐标随意移动。需要对每个坐标锁对应的资源上锁。

解决办法如下:

1. 首先对资源做唯一标识。我采用的是一个字符串来标识一个资源。 具体例子里就是坐标"x,y"

2. 一个字符串对应一把锁,由lockManager来控制锁的创建。可以想见,主要是一个key=>lock的map

 

class Lock {
public:
    typedef boost::shared_ptr<Lock> ptr;

public:
    Lock( const std:: string& key)
        : m_locked( false), m_key(key)
    {}

    ~Lock();
     void  lock();
     void unlock();

private:
    Mordor::FiberMutex m_mutex;
     bool m_locked;
    std:: string m_key;
};

class ScopedLock {
public:
    ScopedLock() : m_locked( false) {}
    ScopedLock(Lock::ptr  lock);
    ScopedLock&  operator=(Lock::ptr  lock);
    ~ScopedLock();
     void  lock();
     void unlock();
private:
    Lock::ptr m_lock;
     bool m_locked;
};

class LockManager {
private:
     class Deleter {
     public:
         void  operator()(Lock* timerLock);
    };
public:
    typedef boost::shared_ptr<LockManager> ptr;
    typedef std::map<std:: string, Lock::ptr> LOCKS;
    LockManager() {}
     //  acquire a lock on key,
    Lock::ptr  lock( const std:: string& key);
     void unlock( const std:: string& key);

private:
    LOCKS m_locks;
    Mordor::FiberMutex m_mutex;
}; 

其中Lock是对我们项目中的mutex的一个简单封装。mutex可以看做是一个pthread_mutex的替代品

 

 我需要实现一个ScopedLock的功能,有两种选择:

1. 用shared_ptr的Deleter来实现。lockManager的lock() 上锁,然后返回一个 设置了特定Deleter的shared_ptr

   

Lock::ptr LockManager:: lock( const std:: string& key) {
    FiberMutex::ScopedLock checkLock(m_mutex);
    LOCKS::iterator it = m_locks.find(key);
     if (it != m_locks.end()) {
        it->second-> lock();
         return boost::shared_ptr<Lock>(&*(it->second), Deleter());
    }  else {
        Lock::ptr newLock( new Lock(key));
        m_locks[key] = newLock;
        newLock-> lock();
         return boost::shared_ptr<Lock>(&*newLock, Deleter());
    }
}

    这个Deleter就负责unlock()。调用代码形如:

{
    Lock::ptr  lock = lockManager-> lock( " key1 ");

    //do something ....

}// Deleter gets called here

 

 

 

2. 实现一个ScopedLock类

 

这里我选择了后者,原因如下:

 我们经常遇到这种情况,需要手动unlock(),而不是依赖于析构。假设我使用第一种实现。

代码如下:

Lock::ptr  lock = lockManager-> lock( " key1 ");
do something...
if (...) {
  lock->unlock();
  do something  else...
else {
  do soemthing  else...
}
} //  we do not want Deleter to unlock again. this may lead to undefiened behavior

 简单想到的解决方案是设置一个成员变量m_locked,在unlock()里如果m_locked==true,就不unlock了。

但是这样并不解决问题。如果

lock->unlock() 执行之后另一个线程得到执行并将该锁lock住,m_locked变为true, 那本线程里Deleter仍然将再次执行unlock. 这显然不是我想要的行为。

 根本原因是shared_ptr其实和原生指针在行为上完全一样。没有办法修改Deleter, 也没有办法给shared_ptr添加数据成员。添加一个m_locked到Lock对象是徒劳的,因为它对多个线程都可见。只有用一个线程本地的数据来标识才有用。

而实现一个ScopedLock就解决了这个问题。Lock::ptr是多线程共享可见的,而ScopedLock不是。


 

 另外,注意到我的ScopedLock有一个无参构造函数。这是有意义的。

我们都知道,一次上多个琐时,需要按照特定的顺序。一个函数里先A后B,另一个函数里先B后A。必然导致死锁。 对于lockManager,这也是一个问题。应为需要锁的资源本来就是未知的,我们怎么决定对多个这种资源上锁时的顺序呢? (按照字符串大小)

假定需要上两个锁。


std:: string key1 = ...;

std:: string key2 = ...;

if (key1 > key2)  {

 ScopedLock lock1(lockManager-> lock(key1));

 ScopedLock lock2(lockManager-> lock(key2));

   do something...

else {

 ScopedLock lock1(lockManager-> lock(key2));

 ScopedLock lock2(lockManager-> lock(key1));
   do something...

}

似乎没什么问题...但是考虑 两个分支里的do something可能是一样的代码,我可不想写重复的两份(也许可以用函数,但不是所有代码都可以或需要改成函数的)。

 

于是无参构造函数和operator=发挥功效了。。。

ScopedLock lock1;
ScopedLock lock2;

if (key1 > key2) {
  lock1 = lockManager-> lock(key1);
  lock2 = lockManager-> lock(key2);
else {
  lock2 = lockManager-> lock(key2);
  lock1 = lockManager-> lock(key1);
}

do something...

 

 至此,lockManager实现完毕。

 

转载于:https://www.cnblogs.com/nocooldown/archive/2011/12/22/2298520.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值