Coffee_Candy

146 分类: 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

评论