ICE实现线程安全之监控器


互斥体实现的是一种简单的互斥机制,在任一时刻,只允许一个线程临
界区中活动(在使用读写互斥体的情况下,是一个写入者线程或多个读取者线程)
。特别地,要让一个线程进入临界区,另一个线程就必须离开它。
这意味着,在使用互斥体时,要做到这样的事情是不可能的:在临界区内
挂起一个线程,过一段时间再唤醒它(例如,在某个条件变成真时)。

为了解决这一问题,Ice提供了监控器。简单地说,监控器是一种用于
保护临界区的同步机制:和互斥体一样,同一时刻在临界区内,只能有一
个线程在活动。但是,监控器允许你在临界区内挂起线程;这样,另一个
线程就能进入临界区。第二个线程可以离开监控器(从而解除监控器的加
锁),或者在监控器内挂起自己;不管是哪一种情况,原来的线程都会被
唤醒,继续在监控器内执行。这样的行为可以扩展到任意数目的线程,所
以在监控器中可以有好几个线程挂起。

与互斥体相比,监控器提供的互斥机制更为灵活,因为它们允许线程检
查某个条件,如果条件为假,就让自己休眠;该线程会由其他某个改变了

该条件的线程唤醒。


namespace IceUtil {
    template
    class Monitor {
    public:
        void lock() const;
        void unlock() const;
        bool tryLock() const;
        void wait() const;
        bool timedWait(const Time&) const;
        void notify();
        void notifyAll();
        typedef LockT > Lock;
        typedef TryLockT > TryLock;
    };
}
请注意,Monitor是一个模板类,需要你用Mutex或RecMutex
做它的模板参数(用RecMutex实例化Monitor,得到的监控器是递归的)。



下面是各成员函数的行为:
lock:这个函数尝试锁住监控器。
如果监控器已被另外的线程锁住,发出调用的线程就会挂起,直到监控器可用为止。
在调用返回时,监控器已被它锁住。

tryLock:这个函数尝试锁住监控器。
如果监控器可用,调用就锁住监控器,返回true。
如果监控器已被另外的线程锁住,调用返回false。

unlock:这个函数解除监控器的加锁。
如果有另外的线程在等待进入监控器(也就是阻塞在lock调用中),
其中一个线程会被唤醒,并锁住监控器。

wait:这个函数挂起发出调用的线程,同时释放监控器上的锁。
其他线程可以调用notify或notifyAll来唤醒在wait调用中挂起的线程。
当wait调用返回时,监控器重被锁住,而挂起的线程会恢复执行。


notify这个函数唤醒目前在wait调用中挂起的一个线程。
如果在调用notify时没有这样的线程,通知就会丢失
(也就是说,如果没有线程能被唤醒,对notify的调用不会被记住)。

请注意,发出通知并不会致使另外的线程立即运行。只有当发出通
知的线程调用wait、或者解除监控器的加锁时,另外的线程才会得以运行

notifyAll:这个函数唤醒目前在wait调用中挂起的所有线程。
和notify一样,如果这时没有挂起的线程,对notifyAll的调用就会丢失。
和notify的情况一样,在使用notifyAll时,只有当发出通知的线程调用wait
、或者解除监控器的加锁时,其他线程才会得以运行


15.9.2
使用监控器
为了说明监控器的使用方法,考虑一个简单的无界队列。一些生产者线
程往队列中增加数据项,一些消费者线程从队列中移除数据项。如果队列
变空,消费者必须等待生产者把新的数据项放入队列。队列自身是一个临
界区,也就是说,当消费者在移除数据项时,我们不能允许生产者把数据
项放入队列。下面是这种队列的一个非常简单的实现:

template class Queue {
public:
    void put(const T & item) {
        _q.push_back(item);
    }
    T get() {
        T item = _q.front();
        _q.pop_front();
        return item;
    }
private:
    list _q;
};

你可以看到,生产者调用put方法,把数据项放入队列,
而消费者调用get方法,从队列中取出数据项。
显然,这种队列实现不是线程安全的,
没有什么能阻止消费者从空的队列中取出数据项。

#include
template class Queue
: public IceUtil::Monitor {
public:
    void put(const T & item) {
        IceUtil::Monitor::Lock lock(*this);
        _q.push_back(item);
        notify();
    }
    T get() {
        IceUtil::Monitor::Lock lock(*this);
        while (_q.size() == 0)
            wait();
        T item = _q.front();
        _q.pop_front();
        return item;
    }
private:
    list _q;
};

下面这种版本的队列使用了监控器,
如果队列是空的,它会挂起消费者:
如果队列是满的,它会挂起生产者:
#include
template class Queue
: public IceUtil::Monitor {
public:
    void put(const T & item) {
        IceUtil::Monitor::Lock lock(*this);
        _q.push_back(item);
        notify();
    }
    T get(){
        IceUtil::Monitor::Lock lock(*this);
        while (_q.size() == 0)        wait();
        T item = _q.front();
        _q.pop_front();
        return item;
    }
private:
    list _q;
};

你可能感兴趣的:(客户端代理))