我们需要保护Observerable中的Observers_,因为可能多个线程中的Observer可能会同时注册,或是Observer销毁后它将从Observers_当中被删除,那么这些同时操作vector
因此我们可以想到使用互斥锁来保护Observers_,当Observerable中的方法将操作Observers_时,我们先加锁。显而易见,此互斥锁的生命周期和Observerable的生命周期相同,因此我们将此互斥锁作为Observerable的成员变量。同时我们使用**RAII(资源获取即初始化)**来对互斥锁进行管理,我们创建一个Mutex类,里面有一个互斥锁变量,当Mutex类在构造时,创建互斥锁资源,当Mutex类析构时,destroy互斥锁资源。因为c++提供的操作互斥锁的函数是面向过程的,因此我们将加锁和解锁操作封装在Mutex类中方便调用。
使用MutexGuard来控制加锁解锁为了保证在加锁后使用者不会忘记解锁,我们创建一个MutexGuard类,来控制Mutex类的加锁和解锁。详细做法如下:一个MutexGuard类中保存着一个Mutex类的引用,在MutexGuard构造函数中,首先使用初始化列表初始化这个引用,然后在构造函数结构体中执行Mutex的加锁操作。在MutexGuard的析构函数中执行Mutex的解锁操作,这样就保证了加锁过后一定不会忘记解锁(记着MutexGuard一定要是一个栈上对象,离开作用域后会被自动析构)。我们将MutexGuard设置成Mutex的友元,Mutex不能直接被使用。
Mutex类实现class MutexGuard;
class Mutex{
public:
Mutex():mutex_(PTHREAD_MUTEX_INITIALIZER){}
~Mutex(){
pthread_mutex_destroy(&mutex_);
}
private:
void lock();
void unlock();
friend class MutexGuard;
pthread_mutex_t mutex_;
};
void Mutex::lock(){
pthread_mutex_lock(&mutex_);
}
void Mutex::unlock(){
pthread_mutex_unlock(&mutex_);
}
MutexGuard类实现
class MutexGuard{
public:
explicit MutexGuard(Mutex& mutex):mutex_(mutex){
mutex_.lock();
}
~MutexGuard(){
mutex_.unlock();
}
private:
Mutex& mutex_;
};
此时的Observer是线程安全的吗?
- Observerable指针可能会比Observer的指针先失效,这样的话Observer在析构时调用Observerable的detach操作是非法的。
- Observer是一个基类,update函数是在它的派生类中实现的,析构的顺序是先析构子类,再析构父类,如果在析构一个Observer的派生类时,在正好执行到析构父类的时候,cpu被另一个操作Observerable的线程抢占并执行notify函数,由于这时候Observer基类的析构函数还未调用,没有从Observerable中删除自己,但它的派生类已经析构掉了,因此这时,notify函数通过Observer指针来调用update函数(是派生类的方法)就是错误的。
~Observer(){
observerable_->detach_(this);
}
- 广泛加锁,效率低下(加锁的地方太多了,例如我们在notify函数中加锁,当更新Observer的时候,可能这个时间会很长,而其他线程一直在等待)。
- 如果update函数调用了register_函数,将导致程序崩溃。(一个得到互斥锁的线程再次请求加锁)
其实这个解决方案是很鸡肋的,1.0版本的Observer最主要的问题时,我们无法判断一个裸指针指向的对象是否是存活的。我们在3.0版本再给出一个解决方案。



