栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

Observer2.0-鸡肋的改进的Observer模式 加上Mutex和MutexGuard

我们需要保护Observerable中的Observers_,因为可能多个线程中的Observer可能会同时注册,或是Observer销毁后它将从Observers_当中被删除,那么这些同时操作vector 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版本再给出一个解决方案。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/942761.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号