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

C++单线程和多线程下的观察者模式

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

C++单线程和多线程下的观察者模式

文章目录

前言代码实现-单线程下的观察者模式

被观察者-基类实现被观察者-派生类实现观察者-基类实现观察者-派生类实现测试 代码实现-多线程下的观察者模式

观察者和被观察者的基类被观察者-派生类观察者-派生类测试多线程下的观察者模式-改进

前言

观察者概念介绍见:观察者模式

完整代码见仓库:observer_mode

代码实现-单线程下的观察者模式

被观察者:产品的成本。
观察者:该产品在路边摊的价格和商场的价格。
订阅-发布:当产品成本改变时,产品在不同销售地点的价格随之改变。

被观察者-基类实现

(2.1.0)被观察者,传递给观察者,的内容(被观察者发布的内容,观察者订阅的内容)
(2.1.1)选定存储被观察者的数据结构。
(2.1.2)该抽象基类定义的接口-操作存储被观察者数据结构的方法。
观察者Observer对象,通过registerObs函数,将自己注册到被观察者Subject的数据中心中。当数据改变的时候,Subject对象调用notifyObs函数,通知所有的观察者对象。deleteObs用于删除注册的观察者。

class Subject {
protected:
    double price=0; // (2.1.0)
    std::vector observers; // (2.1.1)
public:
    virtual void registerObs(Observer* ) = 0; // (2.1.2)
    virtual void deleteObs(Observer* ) = 0;
    virtual void notifyObs() = 0;
    virtual ~Subject() = default; 

    double setPrice(double p) {
        price = p;
    }
    int getPrice() {
        return price;
    }
};
被观察者-派生类实现

实现上面抽象基类定义的接口。(被观察者可以不需要基类,可以直接在上面进行具体实现。这里为了清晰显示接口,故分开来写。)

class Product : public Subject {
public:
    void registerObs(Observer* obs) override{
        observers.push_back(obs);
    }
    void deleteObs(Observer* obs) override{
        auto it = std::find(observers.begin(), observers.end(), obs);
        if(it != observers.end())
            observers.erase(it);
    }
    void notifyObs() override {
        for(auto obs : observers){
            obs->update(price);
        }
    }
};
观察者-基类实现

(2.3.1)观察者基类。

class Observer {
protected:
    double price=0;
public:
    virtual void update(double) = 0;
    virtual ~Observer() = default;
};
观察者-派生类实现

update函数:派生类接收从被观察者传递过来的内容,并进行处理。

// 派生类,东西在地摊的价格-随着成本的波动
class streetProduct : public Observer {
public:
    void update(double cost) override {
        price = cost*1.5;
        std::cout<<"The procude's cost is "< 
测试 

被观察者product将obs1和obs2注册到自己的数据结构中。当它们关心的数据通过setPrice方法,发生改变的时候,product通知所有的订阅者(obs1,obs2)。

int main(void){
    Product product;
    streetProduct obs1;
    marketProduct obs2;
    product.registerObs(&obs1);
    product.registerObs(&obs2);

    product.setPrice(100); // 成本设置为100
    product.notifyObs(); // 通知obs1和obs2
}

运行结果如下:

The procude's cost is 100;When it appear in street stall, It's price is 150
The procude's cost is 100;When it appear in market, It's price is 180

代码实现-多线程下的观察者模式

上面的代码不能用于多线程,因为不能让多线程操作一个没有同步的数据结构。

上面的代码是被观察者调用注册方法,将观察者注册进去。或许应该,被观察者公布注册接口,观察者自己将自己注册进去。

观察者和被观察者的基类

(3.1.1)表示被观察者-观察者之间传递的数据类型为double。

// 观察者基类
class Observer {
public:
    virtual void update(double) = 0; //(3.1.1)
    virtual ~Observer() = default;
};


// 基类(被观察者)
class Subject {
protected:
    std::vector observers;
public:
    virtual void registerObs(Observer* ) = 0;
    virtual void deleteObs(Observer* ) = 0;
    virtual void notifyObs() = 0;
    virtual ~Subject() = default; 
};
被观察者-派生类

(3.2.1)mutex类是能用于保护共享数据免受从多个线程同时访问的同步原语。
(3.2.2)创建lock_guard对象时,它试图接收给定互斥的所有权。控制离开创建 lock_guard 对象的作用域时,销毁 lock_guard 并释放互斥。

class Product : public Subject {
private:
    int price=0;
    std::mutex mutex_; //(3.2.1)
public:
    void registerObs(Observer* obs) override{
        std::lock_guard guard(mutex_); // (3.2.2)
        observers.push_back(obs);
    }
    void deleteObs(Observer* obs) override{
        std::lock_guard guard(mutex_);
        auto it = std::find(observers.begin(), observers.end(), obs);
        if(it != observers.end())
            observers.erase(it);
    }
    void notifyObs() override {
        std::lock_guard guard(mutex_);
        for(auto obs : observers){
            obs->update(price);
        }
    }

    void setPrice(int p) {
        price = p;
    }
    int getPrice() {
        return price;
    }
};
观察者-派生类

(3.3.1)观察者在构造函数中,将自己注册到观察者中。
(3.3.2)观察者在最后一层的派生类的析构函数中,将自己从被观察者中删除。(如果在基类析构函数中进行注册者的删除,是有问题的。eg:派生类先析构了,但是基类还没来得及析构,此时被观察者调用了update函数,这是很糟糕的。但是从类的功能划分来说,析构的任务似乎放在基类中更合适。)

// 派生类,东西在地摊的价格-随着成本的波动
class streetProduct : public Observer {
private:
    double price=0;
    Product& product;
public:
    streetProduct(Product& p): product(p) { //(3.3.1)
        product.registerObs(this);
    };

    void update(double cost) override {
        price = cost*1.5;
        std::cout<<"The procude's cost is "< 
测试 

(3.4.1)类thread表示单个执行线程。
由于锁的存在,该多线程可以安全运行。

void addObs1(Product& p){
    streetProduct obs1(p);
    p.setPrice(100);
    p.notifyObs();
}

int main(void){
    Product product; // 提供存储功能。当数据变动时,提供通知。通知的具体形式,由各自的观察者实现

    thread t1(addObs1,ref(product)); //(3.4.1)
    
    marketProduct obs2(product); // 创建时候,构造函数将自己注册到被观察者对象中;消除的时候,析构函数将自己从被观察者对象中删除
    product.setPrice(100); // 成本设置为100
    product.notifyObs(); // 通知obs1和obs2

    t1.join();
}

运行结果:

The procude's cost is 100;When it appear in market, It's price is 180
The procude's cost is 100;When it appear in market, It's price is 180
The procude's cost is 100;When it appear in street stall, It's price is 150
多线程下的观察者模式-改进

核心思路(使用智能指针):将被观察者中,存储注册观察者的数据结构,改成std::vector>。

参考代码:linux多线程服务器编程-observe

参考视频:C++新手向:手把手教你写一个线程安全的Observer模式-B站

今天1/30日,中午和同学去吃了华莱士,晚上吃年夜饭。^_^。

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

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

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