Coffee_Candy

271 分类: Coffee_Candy读muduo读书笔记,C++boost后端

第一章节线程安全的对象生命周期管理

这里其实感觉没什么好说的主要记录一下读的时候的一些问题

1.1.2mutexlock和mutexlockguard

这部分的封装在2.4有写,还没看,后面补上,emm我平常也就是{lockguard}这样子写一下锁

1.1.3线程安全的Counter

这里是真学到了,boost::noncopyable,继承之后可以把拷贝构造和赋值构造干掉
可以看看源码
2024-11-01T13:41:07.png
emm因为没有看到后面我就用自己的写法写了一下

#include <iostream>
#include <mutex>
#include <memory>
#include <boost/noncopyable.hpp>
class Counter : boost::noncopyable//可以把拷贝构造和赋值构造给干掉
{
public:
    Counter() :_value(0)
    {

    };
    ~Counter()
    {

    };
    int value() 
    {
        {
            std::lock_guard<std::mutex> mylockguard(mtx);
            return _value;
        }
    };
    int getAndIncrease()
    {
        {
            std::lock_guard<std::mutex> mylockguard(mtx);
            int ret = _value++;
            return ret;
        }
    }
private:
    std::mutex mtx;
    int _value;
};

这里是线程安全的因为对_value的操作都是在锁的区域内

1.2对象创建的线程安全

简单来说就是在构造的时候不要暴露this,不然this可能还没构造完就被访问到,所以用二段的写法
2024-11-01T14:02:08.png

1.3销毁太难

大概就是说如果说一对象销毁的时候,他持有的mutex会先死掉,没办法保证析构的线程安全,所以没有办法用mutex解决这个问题
后面也说了析构其实不需要去保护,只有所有线程都访问不到了,析构才是安全的,个人感觉就是智能指针可以处理掉这个问题
然后提到了调用的一种死锁情况swap(a,b),swap(b,a),在并发环境下有可能死锁
2024-11-01T14:13:11.png
解决办法就是比较两个对象的mutex的地址,小或者大的先加锁,反正自己规定

1.4-1.8观察者模式线程安全写法已经C++11后引入的shared_ptr/weak_ptr

提到了观察者模式的多线程情况下的线程安全问题(观察者模式就算,某个对象修改时,其他对象都知道他的修改)
1.Observerable不知道Obsever是不是还活着
2024-11-01T14:22:31.png
2.就算在Obsrever析构的时候去让Observerable解绑,又有一个问题是Observerable是不是还活着,在解绑的时候还没来得及析构掉有没有可能就已经被调用了
2024-11-01T14:26:32.png
然后这里就可以引入shared_ptr/weak_ptr,weak_ptr可以判断对象是不是已经被析构
代码如下

class Observer
{
public:
    Observer();
    ~Observer();
    virtual void update() = 0;
private:
};

class  Observerable
{
public:
     Observerable();
    ~ Observerable();
    void notifyObservers() 
    {
        {
            std::lock_guard<std::mutex> mylockguard(mtx);
            int i = 0;
            for (std::weak_ptr<Observer> obs : observers)
            {
                std::shared_ptr<Observer> newobs(obs.lock());
                if (newobs)newobs->update();
                else observers.erase(observers.begin() + i);
                i++;
            }
        }
    };
    void register_(std::weak_ptr<Observer>x) {};
private:
    std::mutex mtx;
    std::vector<std::weak_ptr<Observer>> observers;
};

提升之后>=2是因为observers里面有一个,obs.lock()提升之后引用计数器+1,就是2了如果还有其他地方在用他那就是>2了
然后就是std::vector<std::weak_ptr> observers;不能改成shard_ptr的因为没有lock这个方法,就不知道对象是不是活着,share_ptr管理内存是没问题,但是问题在于,share_ptr不会让你知道它管理的对象寄了没有,但是讲到了回调函数的事,感觉可以改,但是感觉必要性不大,还有就是share_ptr会自己管理内存,放到Vector里面的话就不会被销毁掉,应该是一直存在的

信号和槽的写法看不懂,打算看看b站的视频之后补上

1.9-1.10 share_ptr

share_ptr本身线程安全,但是他管理的东西不是线程安全的,也就是说读写要加锁
技术陷阱意外延长生命,比如说循环引用,又比如上面那个std::vector<std::weak_ptr> observers改成shard_ptr
在share_ptr出现后的C++RAII技术应该是每个资源分配也就是new应该交给一个回调也就是shard_ptr

1.11对象池-1.12enable_shared_from_this

当涉及多对象时,如果每次都重新创建一个多少有点浪费时间,so建立一个对象池,要的时候创建进去,存着,直到没人用自己再析构掉
2024-11-01T15:34:15.png
这里就涉及到了之前的问题std::vector<std::weak_ptr> observers改成shard_ptr会一直无法销毁vector中的对象,所以应该改成weak_ptr的写法,然后每次.lock判断是否存在,不存在就新建
2024-11-01T15:39:04.png
但是还是有问题map中的weak_ptr没有被干掉,虽然他的指向的对象已经寄了
这里就引出了shard_ptr的销毁回调
2024-11-01T15:42:33.png
但是这里的this是有问题的,有可能stockfactory先于stock析构掉,那这个this就是不知道什么玩意了
这个时候就需要share_from_this去延长stockfactory的生命
2024-11-01T15:48:50.png
这里我写了个std标准库版本的,和boost版本有点差异
emm然后一开始我觉得应该在removestock的时候也把stock的内存释放掉,回调里则是判断对象池还在不在,再去考虑释放是调用removestock还是直接delete,但是仔细想一下对象实际是被一个share_ptr管理的,我们应该只有在它引用计数器=0时才考虑干掉它,也就是在除回调回调之外的地方不要delete,因为它有可能在多线程情况下移出了map但是还有其他线程在用它,引用计数器>0,如果在移出map的时候去delete那么会出现宕机的情况,所以在C++11share_ptr出现的时代,内存管理不由我们程序员乱来,share_ptr是几乎完美的解决,不需要我们思考new和delete的配对

//对象定义
class Stock
{
public:
    Stock(std::string key) {
        attribute = key;
    }
    std::string attribute;
};
//对象池
class StockFactory:public boost::noncopyable,std::enable_shared_from_this<StockFactory>
{
public:
    StockFactory();
    ~StockFactory();

    std::shared_ptr<Stock> get(const std::string& key)
    {
        std::shared_ptr<Stock> pstock;
        {
            std::lock_guard<std::mutex> mylockguard(mtx);
            //注意是引用的
            std::weak_ptr<Stock>& wkstock = stocks_[key];//拿到对应的管理Stock的弱引用指针
            pstock = wkstock.lock();//升级
            //升级失败的时候就是要重新生成一个
            if (!pstock)
            {
                //绑定对应的回调函数,std的和boost不太一样,要指定函数来源哪里就是要加一个this
                pstock.reset(new Stock(key), std::bind(&StockFactory::weakdeletecallback,this,
                    std::weak_ptr<StockFactory>(shared_from_this()), std::placeholders::_1));
                wkstock = pstock;
            }
            return pstock;
        }
    }

private:
    std::mutex mtx;
    std::unordered_map<std::string, std::weak_ptr<Stock>> stocks_;
    void weakdeletecallback(const std::weak_ptr<StockFactory>& wkFactory, Stock* stock)
    {
        //升级
        std::shared_ptr<StockFactory>factory(wkFactory.lock());
        if (factory)
        {
            //升级成功就是对象池还存在,可以删除掉对应的对象,否则就已经没有在map里面
            factory->removestock(stock);
        }
        else
        {
            //把对象空间释放
            delete stock;
        }
    }
    void removestock(Stock* stock)
    {
        if (stock)
        {
            {
                //把map中的删掉
                std::lock_guard<std::mutex> mylockguard(mtx);
                stocks_.erase(stock->attribute);
                            //delete stock这个是不对的,不要delete它
            }
        }
    }
};

#none

作者: Coffee_Candy

版权: 除特别声明,均采用BY-NC-SA 4.0许可协议,转载请表明出处

目录Content

已有 3 条评论

  1. 新车上路,只带前10个人coinsrore.com

  2. 2025年10月新盘 做第一批吃螃蟹的人coinsrore.com
    新车新盘 嘎嘎稳 嘎嘎靠谱coinsrore.com
    新车首发,新的一年,只带想赚米的人coinsrore.com
    新盘 上车集合 留下 我要发发 立马进裙coinsrore.com
    做了几十年的项目 我总结了最好的一个盘(纯干货)coinsrore.com
    新车上路,只带前10个人coinsrore.com
    新盘首开 新盘首开 征召客户!!!coinsrore.com
    新项目准备上线,寻找志同道合 的合作伙伴coinsrore.com
    新车即将上线 真正的项目,期待你的参与coinsrore.com
    新盘新项目,不再等待,现在就是最佳上车机会!coinsrore.com
    新盘新盘 这个月刚上新盘 新车第一个吃螃蟹!coinsrore.com

  3. 果博东方客服开户联系方式【182-8836-2750—】?薇- cxs20250806】
    果博东方公司客服电话联系方式【182-8836-2750—】?薇- cxs20250806】
    果博东方开户流程【182-8836-2750—】?薇- cxs20250806】
    果博东方客服怎么联系【182-8836-2750—】?薇- cxs20250806】

评论