C++线程与并发(Ice3.4.2)
概述
Ice服务器是多线程模型的。在涉及资源的访问和操作的时候将要考虑同步访问机制。
Ice线程库提供了一些与线程有关的抽象:
互斥体,递归互斥体,读写递归互斥体,监控器,一个线程抽象,允许开发者创建,控制,销毁线程。
1.互斥体(The Mutex Class)
1.1)互斥体的定义
IceUtil::Mutex类提供了简单的非递归互斥机制,其定义如下:
namespaceIceUtil { enum MutexProtocol { PrioInherit, PrioNone }; class Mutex { public: Mutex(); Mutex(MutexProtocol p); ~Mutex(); void lock() const; /*lock 函数尝试获取互斥体。如果互斥体已经锁住,它就会挂起发出调用的线程(calling thread),直到互斥体变得可用为止*/ bool tryLock() const;/*trylock函数尝试获取互斥体。如果互斥体未被锁住则返回true,否则直接返回false*/ void unlock() const; /*尝试解除互斥体的加锁*/ typedef LockT<Mutex> Lock; typedef TryLockT<Mutex>TryLock; };
1.2)使用互斥类
假设有一个FileSystem类和write的函数如下:
#include<IceUtil/Mutex.h> namespaceFilesystem { class FileI : virtual public File, virtual public Filesystem::NodeI { public: // ... private: Lines _lines; IceUtil::Mutex _fileMutex; //互斥锁 }; // ... } void Filesystem::FileI::write(const Filesystem::Lines &text,const Ice::Current &) { _fileMutex.lock(); _lines = text; //if(somecondition)return ; _fileMutex.unlock(); }
然而这种加入互斥机制的方法并不好,例如对互斥体加锁了但在函数返回时并没有实现解锁操作,这种情况下就引发死锁情况。
因此我们建议使用Ice提供的两个助手类Lock和TryLock,如下:
voidSomeClass::someFunction(/* params here... */) { IceUtil::Mutex::Locklock(_mutex); // 对mutex对象加锁 // Lots of complexcode here... if (someCondition) { return; // No problem } //... } // 此处调用Mutex类对象的析构函数,同时会解除互斥锁的加锁状态。
2.递归互斥体(The C++ RecMutex Class)
上面所介绍的互斥体是非递归性质的,也就是说他们不能被多次加锁,即使是已经拥有该所的线程也不行。这样会给一些情况带来不便
IceUtil::Mutex_mutex; void f1() { IceUtil::Mutex::Lock lock(_mutex); // ... } void f2() { IceUtil::Mutex::Locklock(_mutex); f1(); // Deadlock! // ... }
为了解决这个问题,Ice同样也提供了递归互斥锁,如下示例:
#include <IceUtil/RecMutex.h> IceUtil::RecMutex _mutex; // Recursive mutex void f1() {
IceUtil::RecMutex::Lock lock(_mutex); //如果该互斥体已被其他线程加锁,那么该线程将会被挂起 // ... } void f2() { IceUtil::RecMutex::Lock lock(_mutex); f1(); // Fine //... }
3. 读写递归互斥体(The RWRecMutex Class)
由于递归互斥体无论是在读取还是写操作的情况下,都是将其并发线程访问序列化。但是读取资源的线程并不会修改所访问的内容;因此让多个读取线程并行拥有互斥体,而同一时刻只能有一个写入的线程获取互斥体。
下面是该读写互斥类的定义:
namespaceIceUtil { class RWRecMutex { public: void readLock() const; bool tryReadLock() const; bool timedReadLock(const Time&) const; void writeLock() const; bool tryWriteLock() const; bool timedWriteLock(const Time&) const; void unlock() const; void upgrade() const; bool timedUpgrade(const Time&) const; typedef RLockT<RWRecMutex>RLock; typedefTryRLockT<RWRecMutex> TryRLock; typedef WLockT<RWRecMutex>WLock; typedefTryWLockT<RWRecMutex> TryWLock; }; }
4.定时锁
读写锁提供了一些可使用超时的成员函数。等待的时间量是通过IceUtil::Time类的实例指定的。
5.监控器(The Monitor)
5.1 Monitor类定义
Monitor类在IceUtil::Monitor中定义,如下所示:
namespace IceUtil { template <class T> class Monitor { public: void lock() const; void unlock() const; bool tryLock() const; void wait() const; bool timedWait(constTime&) const; //这个函数挂起调用它的线程,直到指定的时间流逝.如果在超时之前唤醒被挂起的线程, //这个调用就返回true;否则返回false。 void notify(); void notifyAll(); typedefLockT<Monitor<T> > Lock; typedefTryLockT<Monitor<T> > TryLock; }; }
Monitor类相对于互斥体来说,它提供的互斥机制更为灵活,因为他们允许线程检查某一条件,如果条件为假,就让自己休眠;而这线程会让其他某个改变了条件状态的线程唤醒。
关于如何使用该Monitor类,可以参考上一篇文章--C++线程与并发(二)。