【muduo读书笔记】第一章--智能指针

文章全部内容来源于陈硕大佬的《Linux多线程服务端编程 使用muduo网络库》,此为个人读书笔记
讨论多线程安全,通过对观察者模式的多线程版本的逐步改写,介绍互斥器(mutex)、智能指针作用和用法

智能指针解决了什么问题?:
构造函数只要不泄漏this指针,加锁就可以保证线程安全;
析构则不然,会出现A线程析构对象的时候,B正好在访问该对象,且在B判断该对象是否被释放的时候,A还没来得及释放,这样B就会访问一个混乱的地址内容。(空悬指针)

引入智能指针的概念,其实就是引用技术,中间加了一个中间层proxy,这个proxy包含一个指向真是对象的指针,和一个引用计数器,帮助做垃圾回收。
– weak_ptr:不控制对象生命周期,但能检测对象是否还活着。如果活着这可以提升为有效的shared_ptr。如果死了提升就失败,范围一个空的shared_ptr。
– shared_ptr:强引用,控制对象生命期。

MutexLockGuard lock(mutex_);
lock的析构会晚于返回对象的构造,因此有效地保护了这个共享数据。

weak_ptr解决观察者模式多线程问题:
1.原始观察者模式:

//可观察者类
class Observable{
    
    
public:
    void register_(Observer* x);
    void unregister(Observer* x);

    void notifyObservers(){
    
    
        for(Observer* x : observers_){
    
    
            x->updata(); //***
        }
    }

private:
    vector<Observer*> observers_;
};

//观察者
class Observer{
    
    
public:
    Observer(Observable* s){
    
    
        s->register_(this);
        subject_ = s;
    }
    
    ~Observer(){
    
    
        subject_ ->unregister(this);  //*****
    }
    
    Observable* subject_;
};

存在问题:
A线程执行到26行**处时,没有来得及注销掉当前的观察者,而同时有个B线程执行到9行,即处,正好指向A正在析构的对象,这样就会更新一个正在析构的对象,会发生什么难以预计。

2.引入weak_ptr解决Observer竞态问题
用智能指针改写Observable类

class Observable{
    
    
public:
    void register_(weak_ptr<Observer> x);
    void notifyObservers();

private:
    mutable MutexLock mutex_;
    std::vector<weak_ptr<Observer>> observers_;
    typedef std::vector<weak_ptr<Observer>>::iterator Iterator;
};

void Observable::notifyObservers() {
    
    
    MutexLockGuard lock(mutex_); //加锁
    Iterator it = observers_.begin(); //weak_ptr迭代器
    while(it != obervers_.end()){
    
    
        shared_ptr<Observer> obj(it->lock()); //尝试提升,这一步是线程安全的
        if(obj){
    
    
            //提升成功
            obj->update();  //***
            ++it;
        }else{
    
    
            //提升失败,从容器中拿掉weak_ptr
            it-<observers_.erase(it);
        }
    }
}

存在问题,第19行,***处。如果Update之中调用了register或者unregister,且mutex不可重入,则可能会死锁。(注:可重入 就是说某个线程已经获得某个锁,可以再次获取锁而不会出现死锁。)

3.完全解决Oberver模式问题 – 绕开它
使用Signal/Slots,详见书本p29

shared_ptr特性:
1.因为shared_ptr里面有两个成员,一个是计数器一个是指向底层对象的指针。所以shared_ptr本身没法搞原子操作,是线程不安全的。(但它指向的对象是线程安全的)。
即–
一个shared_ptr对象实体可被多个线程同时读取。
两个shared_ptr对象实体可以被两个线程同时写入,析构算写操作。
如果要从多个线程读写同一个shared_ptr对象,那么需要加锁。

2.shared_ptr要小心复制,如vector中的,boost::bind中的

class StockFactory:boost::noncopyable{
    
    
public:
    shared_ptr<Stock> get(const string& key);

private:
    mutable MutexLock mutex_;
    std::map<string,shared_ptr<Stock>> stocks_;
};

因为map里面的值是shared_ptr,所以stock永远有引用,永远不会被销毁。

所以需要用weak_ptr做弱绑定,且重写get函数。
下面这段代码就是弱回调。

class StockFactory:boost::noncopyable{
    
    
public:
    shared_ptr<Stock> get(const string& key);

private:
    mutable MutexLock mutex_;
    std::map<string,shared_ptr<Stock>> stocks_;

    void deleteStock(Stock* stock){
    
    
        if(stock){
    
    
            MutexLockGuard lock(mutex_);
            stocks_erase(stock->key());
        }
        delete stock;
    }
};

shared_ptr<Stock> StockFactory::get(const string &key) {
    
    
    shared_ptr<Stock> pStock;
    MutexLockGuard lock(mutex_); //加锁保护智能指针本身 ??其实不确定对不对
    weak_ptr<Stock> &wkStock = stocks_[key];//如果key不存在,则会默认构造一个
    pStock = wkStock.lock(); //尝试提升,如果有这个key则直接返回

    //否则需要删除map里面的值
    if(!pStock){
    
    
        pStock.reset(new Stock(key),boost::bind(&StockFactory::deleteStock,shared_form_this(),_1));
        wkStock = pStock;
    }

    return pStock;
}

这部分代码主要是get这边用weak_ptr做了个提升检测,还有就是reset这里,做了个在没有引用时删除map的操作。
特别的:

shared_form_this() --》指向当前对象的shared_ptr的对象,类似于this

最后:作者建议少用跨线程对象

Guess you like

Origin blog.csdn.net/abyss_miracle/article/details/121586807