线程安全的Observer模式2.0-鸡肋的改进的Observer模式

Observer2.0-鸡肋的改进的Observer模式

加上Mutex和MutexGuard

我们需要保护Observerable中的Observers_,因为可能多个线程中的Observer可能会同时注册,或是Observer销毁后它将从Observers_当中被删除,那么这些同时操作vector<Observer*> observers_的操作是线程不安全的,我们需要保证在一个时刻只能有一个线程能操作observers_

使用Mutex作为Observerable成员变量

因此我们可以想到使用互斥锁来保护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版本再给出一个解决方案。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值