C++线程与并发(Ice3.4.2)
Ice服务器是多线程模型的。在涉及资源的访问和操作的时候将要考虑同步访问机制。
Ice线程库提供了一些与线程有关的抽象:
互斥体,递归互斥体,读写递归互斥体,监控器,一个线程抽象,允许开发者创建,控制,销毁线程。
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 Lock;
typedef TryLockTTryLock;
};
假设有一个FileSystem类和write的函数如下:
#include
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类对象的析构函数,同时会解除互斥锁的加锁状态。
上面所介绍的互斥体是非递归性质的,也就是说他们不能被多次加锁,即使是已经拥有该所的线程也不行。这样会给一些情况带来不便
IceUtil::Mutex_mutex;
void f1()
{
IceUtil::Mutex::Lock lock(_mutex);
// ...
}
void f2()
{
IceUtil::Mutex::Locklock(_mutex);
f1(); // Deadlock!
// ...
}
为了解决这个问题,Ice同样也提供了递归互斥锁,如下示例:
#include
IceUtil::RecMutex _mutex; // Recursive mutex
void f1()
{
IceUtil::RecMutex::Lock lock(_mutex); //如果该互斥体已被其他线程加锁,那么该线程将会被挂起
// ...
}
void f2()
{
IceUtil::RecMutex::Lock lock(_mutex);
f1(); // Fine
//...
}
由于递归互斥体无论是在读取还是写操作的情况下,都是将其并发线程访问序列化。但是读取资源的线程并不会修改所访问的内容;因此让多个读取线程并行拥有互斥体,而同一时刻只能有一个写入的线程获取互斥体。
下面是该读写互斥类的定义:
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 RLockTRLock;
typedefTryRLockT TryRLock;
typedef WLockTWLock;
typedefTryWLockT TryWLock;
};
}
读写锁提供了一些可使用超时的成员函数。等待的时间量是通过IceUtil::Time类的实例指定的。
5.1 Monitor类定义
Monitor类在IceUtil::Monitor中定义,如下所示:
namespace IceUtil {
template
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 > Lock;
typedefTryLockT > TryLock;
};
}
Monitor类相对于互斥体来说,它提供的互斥机制更为灵活,因为他们允许线程检查某一条件,如果条件为假,就让自己休眠;而这线程会让其他某个改变了条件状态的线程唤醒。
关于如何使用该Monitor类,可以参考上一篇文章--C++线程与并发(二)。