一个简单对象池的迭代开发学习记录

0.前言

本文仔细实践了陈硕多线程服务端编程一书中第一章对象池的例子,这个实践可以说是对shared_ptr,weak_ptr,shared_from_this,弱回调,多线程开发的一个综合应用.由简单到复杂,针对具体问题,逐步迭代优化,是一个相当不错的例子.这里记录下详细的迭代过程.方便以后查阅.
说明:

  1. StockFactory理论上应该是单例的(这里禁用了copy来简单表示只有一个实例,所以其中的stocks_多线程下存在竞争条件),而且整个程序运行期间不应该销毁(和版本6,7有关)
    2.Stock类
class Stock:boost::noncopyable{
public:
    Stock(string strName){
        strName_=strName;
        cout<<" Create Stock["<<strName_<<"]"<<endl;
    }
    ~Stock(){
        cout<<"Destroy Stock["<<strName_<<"]"<<endl;
    }
    string stockName() const { return strName_;}
private:
    string strName_;
};

1.版本1:容器中存放shared_ptr,产生无法销毁的对象

版本1中 股票池 std::map<string,shared_ptr> stocks_ 中使用了shared_ptr,每次将股票加入哈希表中时都会增加引用计数,导致每只股票的引用计数无法清零,即股票对象在生命周期结束后无法被销毁.
所以容器中一般不要存放shared_ptr,而改用weak_ptr

namespace Version1{
    //
    class StockFactory:boost::noncopyable{
    public:
        //如果要获取的股票不存在就创建一只这样股票并加入到哈希表中
        shared_ptr<Stock> get(const string& key);
        void dumpRefCount(){
            for(auto &item : stocks_){//如果是item会增加一次stocks_中的引用
                cout<<"Stock "<<item.first<<" use_count is "<<item.second.use_count()<<endl;
            }
        }
        size_t stockNum() const{ return stocks_.size();}
    private:
        mutable mutex mutex_;
        //StockFactory应该是一个单例,这里虽然没有用单例,但也禁用了copy,所以stocks_只有一份,多线程是会有竞争
        std::map<string,shared_ptr<Stock>> stocks_;
    };//StockFactory

// 实现1
//    shared_ptr<Stock> StockFactory::get(const string &key) {
//        auto search = stocks_.find(key);
//        shared_ptr<Stock> pStock;
//        //加锁避免多线程竞争stock_
//        std::lock_guard<std::mutex> lock(mutex_);
//        //已经有这个元素了
//        if(search!=stocks_.end()){
//            pStock=stocks_[key];
//        }
//        else{//没有这个元素
//            pStock.reset(new Stock(key));
//            stocks_[key]=pStock;
//        }
//        return pStock;
//    }

    //更简单的实现
    shared_ptr<Stock> StockFactory::get(const string &key) {
        lock_guard<std::mutex> lock(mutex_);
        //从中取出一支股票的引用
        shared_ptr<Stock>& pStock=stocks_[key];
        //空指针说明没有这支股票
        if(nullptr==pStock){
            pStock.reset(new Stock(key));//重新创建一只股票
        }
        return pStock;
    }

    void test(){
        cout<<"hello version 1"<<endl;
        StockFactory stockFactory;
        {
            shared_ptr<Stock> stockHik = stockFactory.get("Hikvison");
            shared_ptr<Stock> stockAli = stockFactory.get("Alibaba");
            stockFactory.dumpRefCount();
            /* std::map<string,shared_ptr<Stock>> stocks_ 中用的是shared_ptr<Stock>所以可以看到
             * 最终两支股票的引用计数都是2
             * Stock Alibaba use_count is 2
             * Stock Hikvison use_count is 2
            */
        }
        stockFactory.dumpRefCount();
        /* std::map<string,shared_ptr<Stock>> stocks_ 中用的是shared_ptr<Stock>所以可以看到
         * 最终两支股票的引用计数都是1 无法被销毁
         * Stock Alibaba use_count is 1
         * Stock Hikvison use_count is 1
        */

    }
}//namespace version 1

2.版本2:只增不减的内存空间(轻微内存泄漏)(weak_ptr)

这个版本用weak_ptr替代了shared_ptr,解决了version 1 中股票对象无法销毁的问题,但这个版本中stocks_的大小只会增加不会减少,存在轻微的内存泄露,股票资源已经释放了,但仍然在stocks_中占据一个空间.

namespace Version2{
    class StockFactory:boost::noncopyable{
    public:
        //如果要获取的股票不存在就创建一只这样股票并加入到哈希表中
        shared_ptr<Stock> get(const string& key);
        void dumpRefCount(){
            for(auto &item : stocks_){//如果是item会增加一次stocks_中的引用
                cout<<"Stock "<<item.first<<" use_count is "<<item.second.use_count()<<endl;
            }
            cout<<"size of stocks = "<<stockNum()<<endl;
        }
        size_t stockNum() const{ return stocks_.size();}
    private:
        mutable mutex mutex_;
        //StockFactory应该是一个单例,这里虽然没有用单例,但也禁用了copy,所以stocks_只有一份,多线程是会有竞争
        std::map<string,weak_ptr<Stock>> stocks_;
    };//StockFactory

    shared_ptr<Stock> StockFactory::get(const string &key) {
        shared_ptr<Stock> pStock;
        lock_guard<std::mutex> lock(mutex_);
        //从中取出一支股票的弱引用
        weak_ptr<Stock>& wkstock=stocks_[key];
        //去尝试提升这个弱引用
        pStock=wkstock.lock();
        if(nullptr==pStock){//提升失败,说明没有这支股票
            pStock.reset(new Stock(key));//重新创建一只股票
            wkstock=pStock;//更新原有的引用
        }
        return pStock;
    }
    void test(){
        cout<<"hello version 2"<<endl;
        StockFactory stockFactory;
        {
            shared_ptr<Stock> stockHik = stockFactory.get("Hikvison");
            shared_ptr<Stock> stockAli = stockFactory.get("Alibaba");

            stockFactory.dumpRefCount();
            /* std::map<string,weak_ptr<Stock>> stocks_ 中用的是weak_ptr<Stock>所以可以看到
             * 最终两支股票的引用计数都是1
             * Stock Alibaba use_count is 1
             * Stock Hikvison use_count is 1
             * size of stocks = 2
            */
        }
        stockFactory.dumpRefCount();
        /* 调用了股票的析构函数
         * Stock Alibaba use_count is 0
         * Stock Hikvison use_count is 0
         * size of stocks = 2
        */
    }
}//namespace version 2

3.版本3:给shared_ptr传入自定义删除器(reset(p,d))

为了解决版本2中的问题,给shared_ptr传入自定义删除器,当股票对象销毁时,就从哈希表中将这个名称的股票给剔除.

pStock.reset(new Stock(key),
        std::bind(&StockFactory::deleteStock,this,std::placeholders::_1));

但这个版本依然有问题,StockFactory::get 方法中的 pStock.reset方法把原始的this(StockFactory) 指针保存到了std::function(std::bind)中(StockFactory::deleteStock),如果StockFactory的生命周期比stock短,那么stock的析构去回调StockFactory::deleteStock时,因为StockFactory已经析构, StockFactory 中的mutex_也被销毁了,这时执行到lock_guardstd::mutex lock(mutex_);结果将是未知的,最好的结果就是崩溃,也可能一直阻塞下去,test_bad()运行结果来看大概率一直阻塞下去,这样出现问题时相当难排查.
自定义删除器是在引用计数为0时自动执行内存删除操作,如果知道且能手动erase掉map中的元素,完全可以在容器中存shared_ptr,erase容器时,内存肯定会释放

namespace Version3{
    class StockFactory/*:boost::noncopyable*/{
    public:
        //如果要获取的股票不存在就创建一只这样股票并加入到哈希表中
        shared_ptr<Stock> get(const string& key);
        void dumpRefCount(){
            for(auto &item : stocks_){//如果是item会增加一次stocks_中的引用
                cout<<"Stock "<<item.first<<" use_count is "<<item.second.use_count()<<endl;
            }
            cout<<"size of stocks = "<<stockNum()<<endl;
        }
        StockFactory(const StockFactory& thr){
            cout<<"copy StockFactory"<<endl;
        }
        StockFactory(){
            cout<<"create StockFactory"<<endl;
        }
        ~StockFactory(){
            cout<<"delete StockFactory"<<endl;
        }
        size_t stockNum() const{ return stocks_.size();}
    private:
        mutable mutex mutex_;
        //StockFactory应该是一个单例,这里虽然没有用单例,但也禁用了copy,所以stocks_只有一份,多线程是会有竞争
        std::map<string,weak_ptr<Stock>> stocks_;
        void deleteStock(Stock* stock){
            if(stock){
                lock_guard<std::mutex> lock(mutex_);
                stocks_.erase(stock->stockName());
            }
            delete stock;
        }
    };//StockFactory

    shared_ptr<Stock> StockFactory::get(const string &key) {
        shared_ptr<Stock> pStock;
        lock_guard<std::mutex> lock(mutex_);
        //从中取出一支股票的弱引用
        weak_ptr<Stock>& wkstock=stocks_[key];
        //去尝试提升这个弱引用
        pStock=wkstock.lock();
        if(nullptr==pStock){//提升失败,说明没有这支股票
            pStock.reset(new Stock(key),
                    std::bind(&StockFactory::deleteStock,this,std::placeholders::_1));//重新创建一只股票
            wkstock=pStock;//更新原有的引用
        }
        return pStock;
    }

    void test(){
        cout<<"hello version 3"<<endl;
        StockFactory stockFactory;
        {
            shared_ptr<Stock> stockHik = stockFactory.get("Hikvison");
            shared_ptr<Stock> stockAli = stockFactory.get("Alibaba");

            stockFactory.dumpRefCount();
            /* std::map<string,weak_ptr<Stock>> stocks_ 中用的是weak_ptr<Stock>所以可以看到
             * 最终两支股票的引用计数都是1
             * Stock Alibaba use_count is 1
             * Stock Hikvison use_count is 1
             * size of stocks = 2
            */
        }
        stockFactory.dumpRefCount();
        /* 调用了股票的析构函数
         * Stock Alibaba use_count is 0
         * Stock Hikvison use_count is 0
         * size of stocks = 0
        */
    }
    void test_bad(){
        cout<<"hello version 3(bad test)"<<endl;

        shared_ptr<Stock> copy;
        {
            //用heap对象才能模拟出阻塞的问题,栈对象不行(本来以为是因为栈对象会导致复制,
            // 可是也传递this指针时没有调用构造,可能是调用了拷贝构造了,测试发现拷贝构造也没有调用,这点不是很明白)
            shared_ptr<StockFactory> stockFactory(new StockFactory());
            {
                shared_ptr<Stock> stockHik = stockFactory->get("Hikvison");
                copy = stockHik;//延长hik的生命周期
                stockFactory->dumpRefCount();

//                    //模拟stockfactory的生命周期比stock短,这样模拟似乎不太好,毕竟要是手动删除的,version 4手动删除也没办法
//                    if(pStockFactory!= nullptr){
//                        delete pStockFactory;
//                    }
            }
        }

    }
}//namespace version 3

4.版本4(一个几乎完整的实现版):使用shared_from_this,管理StockFactory的生命周期

将版本3中的this指针改为shared_ptr.方法是:采用 shared_from_this(),为了使用它,StockFactory不能是stack object 必须是heap object
到此为止,Stockfactory的已经没有问题了,但是Stockfactory的shared_ptr传入了deleteStock中,StockFactory的生命周期被意外延长了(事实上StockFactory应该是个单例,程序运行期间都不应该被销毁,所以不存在生命周期意外延长的问题),所以这个版本可以说是一个完整的实现了

一个多余的考虑(为了举例说明弱引用):
当我们有时需要在deleteStock中实现"如果对象还活着就调用其成员函数,否则忽略之"这样的语义时,就会有问题了,因为deleteStock保存有Stockfactory的shared_ptr指针,Stockfactory会一直存在

namespace Version4{
    class StockFactory:public enable_shared_from_this<StockFactory>
            ,boost::noncopyable{
    public:
        //如果要获取的股票不存在就创建一只这样股票并加入到哈希表中
        shared_ptr<Stock> get(const string& key);
        void dumpRefCount(){
            for(auto &item : stocks_){//如果是item会增加一次stocks_中的引用
                cout<<"Stock "<<item.first<<" use_count is "<<item.second.use_count()<<endl;
            }
            cout<<"size of stocks = "<<stockNum()<<endl;
        }
        size_t stockNum() const{ return stocks_.size();}
    private:
        mutable mutex mutex_;
        //StockFactory应该是一个单例,这里虽然没有用单例,但也禁用了copy,所以stocks_只有一份,多线程是会有竞争
        std::map<string,weak_ptr<Stock>> stocks_;
        void deleteStock(Stock* stock){
            if(stock){
                lock_guard<std::mutex> lock(mutex_);
                stocks_.erase(stock->stockName());
            }
            delete stock;
        }
    };//StockFactory

    shared_ptr<Stock> StockFactory::get(const string &key) {
        shared_ptr<Stock> pStock;
        lock_guard<std::mutex> lock(mutex_);
        //从中取出一支股票的弱引用
        weak_ptr<Stock>& wkstock=stocks_[key];
        //去尝试提升这个弱引用
        pStock=wkstock.lock();
        if(nullptr==pStock){//提升失败,说明没有这支股票
            pStock.reset(new Stock(key),
                         std::bind(&StockFactory::deleteStock,
                                 shared_from_this(),
                                 std::placeholders::_1));//重新创建一只股票
            wkstock=pStock;//更新原有的引用
        }
        return pStock;
    }

    void test(){
        cout<<"hello version 4"<<endl;
        shared_ptr<Stock> copy;//
        {
            //用heap对象才能模拟出阻塞的问题,站对象不行(本来以为是因为栈对象会导致复制,
            // 可是也传递this指针时没有调用构造,可能是调用了拷贝构造了,测试发现拷贝构造也没有调用)
            shared_ptr<StockFactory> stockFactory(new StockFactory());
            {
                shared_ptr<Stock> stockHik = stockFactory->get("Hikvison");
                copy = stockHik;//延长hik的生命周期
                stockFactory->dumpRefCount();
            }
        }
        /* 调用了股票的析构函数
         * Stock Alibaba use_count is 0
         * Stock Hikvison use_count is 0
         * size of stocks = 0
        */
    }
}//namespace version 4

5.版本5:画蛇添足(实践弱引用)

这里使用了boost库,stl库也是可以的,完整代码中版本6即为stl库的实现

namespace Version5{
class StockFactory:public boost::enable_shared_from_this<StockFactory>
            ,boost::noncopyable{
    public:
        //如果要获取的股票不存在就创建一只这样股票并加入到哈希表中
        boost::shared_ptr<Stock> get(const string& key);
        void dumpRefCount(){
            for(auto &item : stocks_){//如果是item会增加一次stocks_中的引用
                cout<<"Stock "<<item.first<<" use_count is "<<item.second.use_count()<<endl;
            }
            cout<<"size of stocks = "<<stockNum()<<endl;
        }
        size_t stockNum() const{ return stocks_.size();}
    private:
        mutable mutex mutex_;
        //StockFactory应该是一个单例,这里虽然没有用单例,但也禁用了copy,所以stocks_只有一份,多线程是会有竞争
        std::map<string,boost::weak_ptr<Stock>> stocks_;
        //注意这里必须是静态函数,原因大概出在bind中传递的不是this或shared_from_this而是一个强制转换的弱引用指针
       static void weakDeleteStockCallback(const boost::weak_ptr<StockFactory>& wkFactory,Stock* stock){
            //wkFactory.expired() wkFactory的use_count==0(即对象已经销毁)返回true
           if(wkFactory.expired()){//这里要注意,陈硕的源码没有,如果没有的话,StockFactory的生命周期短与stock时就会抛出异常
               cout<<"factory died"<<endl;
           }else{
               boost::shared_ptr<StockFactory> factory(wkFactory);//尝试提升,提升失败为null
               if(factory!= nullptr)
               {
                   if(stock!= nullptr){
                       factory->removeStock(stock);
                       delete stock;
                   }
               }
               else{
                   cout<<"factory died"<<endl;
               }

           }

        }

        void removeStock(Stock* stock)
        {
            if (stock)
            {
                lock_guard<std::mutex> lock(mutex_);
                stocks_.erase(stock->stockName());
            }
        }
    };//StockFactory

    boost::shared_ptr<Stock> StockFactory::get(const string &key) {
        boost::shared_ptr<Stock> pStock;
        lock_guard<std::mutex> lock(mutex_);
        //从中取出一支股票的弱引用
        boost::weak_ptr<Stock>& wkstock=stocks_[key];
        //去尝试提升这个弱引用
        pStock=wkstock.lock();
        if(nullptr==pStock){//提升失败,说明没有这支股票
            pStock.reset(new Stock(key),
                         boost::bind(&StockFactory::weakDeleteStockCallback,
                                     boost::weak_ptr<StockFactory>(shared_from_this()),_1));
            wkstock=pStock;//更新原有的引用
        }
        return pStock;
    }

    void test_bad(){
        cout<<"hello version 5(test bad)"<<endl;

        boost::shared_ptr<Stock> copy;//
        {
            //用heap对象才能模拟出阻塞的问题,站对象不行(本来以为是因为栈对象会导致复制,
            // 可是也传递this指针时没有调用构造,可能是调用了拷贝构造了,测试发现拷贝构造也没有调用)
            boost::shared_ptr<StockFactory> stockFactory(new StockFactory());
            {
                boost::shared_ptr<Stock> stockHik = stockFactory->get("Hikvison");
                copy = stockHik;//延长hik的生命周期
                stockFactory->dumpRefCount();
            }
        }

        /* 调用了股票的析构函数
         * Stock Alibaba use_count is 0
         * Stock Hikvison use_count is 0
         * size of stocks = 0
        */
    }

    void test(){
        cout<<"hello version 5"<<endl;

        //用heap对象才能模拟出阻塞的问题,站对象不行(本来以为是因为栈对象会导致复制,
        // 可是也传递this指针时没有调用构造,可能是调用了拷贝构造了,测试发现拷贝构造也没有调用)
        boost::shared_ptr<StockFactory> stockFactory(new StockFactory());
        {
            boost::shared_ptr<Stock> stockHik = stockFactory->get("Hikvison");

            stockFactory->dumpRefCount();
        }

        /* 调用了股票的析构函数
         * Stock Alibaba use_count is 0
         * Stock Hikvison use_count is 0
         * size of stocks = 0
        */
    }
}//namespace version 5

6.版本6(完整代码中的版本7):版本1-5中隐藏的多线程竞争问题

版本4中我们说这是一个基本完整的实现,实际上存在这较难察觉的竞争问题

 void removeStock(Stock* stock)
{
    if (stock)
    {
        lock_guard<std::mutex> lock(mutex_);
        stocks_.erase(stock->stockName());

    }
}

多线程情况下这里会有竞争,Race condition 发生在 StockFactory::deleteStock() 这个成员函数里,如果进入 deleteStock 之后,在 lock 之前,有别的线程调用了相同key 的 StockFactory::get(),会造成此 key 被从 stocks_ 哈希表中错误地删除,因此会重复创建 Stock 对象。程序不会 crash 也不会有 memory leak,但是程序中存在两个相同 key 的 Stock 对象,违背了对象池应有的语意(同一对象的内存应该相同)。
详细解释参考:https://zhuanlan.zhihu.com/p/30522095,这里仅贴一下文中竟态分析图
在这里插入图片描述
在这里插入图片描述
注意:如果把条件 if (it->second.expired()) 改成 if (!it->second.lock()),即试着将 weak_ptr 提升为 shared_ptr,如果提升不成功,则 erase key,这样做有没有问题?
这样做有可能造成死锁,因为 muduo Mutex 是不可重入的。race condition:如果 weak_ptr::lock() 成功,拿到一个 shared_ptr (use_count 应该 > 1),然后在此 shared_ptr 析构之前,其他线程释放了这个对象,使得 use_count 降为 1,那么当此 shared_ptr 析构的时候,会递归调用 deleteStock(),从而造成死锁。(不太明白)
但是我的实现中使用的是std::mutex,暂时没有查到它是否可重入.
在这里插入图片描述

namespace Version7{
    class StockFactory:public std::enable_shared_from_this<StockFactory>
            ,boost::noncopyable{
    public:
        //如果要获取的股票不存在就创建一只这样股票并加入到哈希表中
        std::shared_ptr<Stock> get(const string& key);
        void dumpRefCount(){
            for(auto &item : stocks_){//如果是item会增加一次stocks_中的引用
                cout<<"Stock "<<item.first<<" use_count is "<<item.second.use_count()<<endl;
            }
            cout<<"size of stocks = "<<stockNum()<<endl;
        }
        size_t stockNum() const{ return stocks_.size();}
    private:
        mutable mutex mutex_;
        //StockFactory应该是一个单例,这里虽然没有用单例,但也禁用了copy,所以stocks_只有一份,多线程是会有竞争
        std::map<string,std::weak_ptr<Stock>> stocks_;

        //不是static才能编译过
        static void weakDeleteStockCallback(const std::weak_ptr<StockFactory>& wkFactory,Stock* stock){
            if(wkFactory.expired()){//如果factory的生命周期比stock短就结束,陈硕书上的源码是没有这个判断的
                cout<<"factory died"<<endl;
            }
            else{
                std::shared_ptr<StockFactory> factory(wkFactory);//尝试提升,提升失败为null
                if(factory!= nullptr)
                {
                    if(stock!= nullptr){
                        factory->removeStock(stock);
                        delete stock;
                    }
                }
                else{
                    cout<<"factory died"<<endl;
                }

            }

        }

//        void removeStock(Stock* stock)//有问题的代码
//        {
//            if (stock)
//            {
//                lock_guard<std::mutex> lock(mutex_);
//                stocks_.erase(stock->stockName());
//
//            }
//        }

        void removeStock(Stock* stock)
        {
            if (stock)
            {
                std::lock_guard<std::mutex> lock(mutex_);
                auto it = stocks_.find(stock->stockName());
                assert(it != stocks_.end());
                if (it->second.expired())
                {
                    stocks_.erase(stock->stockName());
                }
            }
        }


    };//StockFactory

    std::shared_ptr<Stock> StockFactory::get(const string &key) {
        std::shared_ptr<Stock> pStock;
        lock_guard<std::mutex> lock(mutex_);
        //从中取出一支股票的弱引用
        std::weak_ptr<Stock>& wkstock=stocks_[key];
        //去尝试提升这个弱引用
        pStock=wkstock.lock();
        if(nullptr==pStock){//提升失败,说明没有这支股票
            pStock.reset(new Stock(key),
                         std::bind(&StockFactory::weakDeleteStockCallback,
                                   std::weak_ptr<StockFactory>(shared_from_this()),
                                   std::placeholders::_1));
            wkstock=pStock;//更新原有的引用
        }
        return pStock;
    }

    void test(){
        cout<<"hello version 7"<<endl;

        {
            //用heap对象才能模拟出阻塞的问题,站对象不行(本来以为是因为栈对象会导致复制,
            // 可是也传递this指针时没有调用构造,可能是调用了拷贝构造了,测试发现拷贝构造也没有调用)
            shared_ptr<StockFactory> stockFactory(new StockFactory());
            {
                std::shared_ptr<Stock> stockHik = stockFactory->get("Hikvison");
                stockFactory->dumpRefCount();
            }
            stockFactory->dumpRefCount();
        }

        /* 调用了股票的析构函数
         * Stock Alibaba use_count is 0
         * Stock Hikvison use_count is 0
         * size of stocks = 0
        */
    }

    void test_bad(){
        cout<<"hello version 7(test bad)"<<endl;

        std::shared_ptr<Stock> copy;//
        {
            //用heap对象才能模拟出阻塞的问题,站对象不行(本来以为是因为栈对象会导致复制,
            // 可是也传递this指针时没有调用构造,可能是调用了拷贝构造了,测试发现拷贝构造也没有调用)
            std::shared_ptr<StockFactory> stockFactory(new StockFactory());
            {
                std::shared_ptr<Stock> stockHik = stockFactory->get("Hikvison");
                copy = stockHik;//延长hik的生命周期
                stockFactory->dumpRefCount();
            }
            stockFactory->dumpRefCount();
        }

    }
}//namespace version 7
#endif

附录

3.参考

1. 对象池的一个 race condition
2.陈硕-linux多线程网咯编程

2.版本6(7)缺陷的验证代码(即对象池的一个race condition的验证代码)

//
// Created by root on 12/28/19.
//

// reproduce race condition of Factory.cc if compiled with -DREPRODUCE_BUG

#include <boost/noncopyable.hpp>
#include <mutex>
#include <thread>
#include <memory>
#include <unordered_map>

#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <iostream>

using namespace std;
using std::string;
#define REPRODUCE_BUG
void sleepMs(int ms)
{
    usleep(ms*1000);
}

class Stock : boost::noncopyable
{
public:
    Stock(const string& name)
            : name_(name)
    {
        printf("%ld: Stock[%p] %s\n", std::this_thread::get_id(), this, name_.c_str());
    }

    ~Stock()
    {
        printf("%ld: ~Stock[%p] %s\n", std::this_thread::get_id(), this, name_.c_str());
    }

    const string& key() const { return name_; }

private:
    string name_;
};


class StockFactory : boost::noncopyable
{
public:

    std::shared_ptr<Stock> get(const string& key)
    {
        std::shared_ptr<Stock> pStock;
        std::lock_guard<std::mutex> lock(mutex_);
        std::weak_ptr<Stock>& wkStock = stocks_[key];
        pStock = wkStock.lock();
        if (!pStock)
        {
            pStock.reset(new Stock(key),
                         [this] (Stock* stock) { deleteStock(stock); });
            wkStock = pStock;
        }
        return pStock;
    }

private:

    void deleteStock(Stock* stock)
    {
        printf("%ld: deleteStock[%p]\n", std::this_thread::get_id(), stock);
        if (stock)
        {
            sleepMs(500);
            std::lock_guard<std::mutex>  lock(mutex_);
#ifdef REPRODUCE_BUG
            printf("%ld: erase %zd\n", std::this_thread::get_id(), stocks_.erase(stock->key()));
#else
            auto it = stocks_.find(stock->key());
            assert(it != stocks_.end());
            if (it->second.expired())
            {
                stocks_.erase(it);
            }
            else
            {
                printf("%ld: %s is not expired\n", std::this_thread::get_id(), stock->key().c_str());
            }
#endif
        }
        delete stock;  // sorry, I lied
    }

    std::mutex mutex_;
    std::unordered_map<string, std::weak_ptr<Stock> > stocks_;
};

void threadB(StockFactory* factory)
{
    sleepMs(250);
    auto stock = factory->get("MS");
    printf("%ld: stock %p\n", std::this_thread::get_id(), stock.get());


    sleepMs(500);
    auto stock2 = factory->get("MS");
    printf("%ld: stock2 %p\n", std::this_thread::get_id(), stock2.get());
    if (stock != stock2)
    {
        printf("WARNING: stock != stock2\n");
    }
}

int main()
{
    StockFactory factory;
    std::thread thr(threadB,&factory);
    {
        auto stock = factory.get("MS");
        printf("%ld: stock %p\n", std::this_thread::get_id(), stock.get());
    }
    thr.join();

}
139741283514176: Stock[0x55d46349f030] MS
139741283514176: stock 0x55d46349f030
139741283514176: deleteStock[0x55d46349f030]
139741265458944: Stock[0x7f1804000b20] MS
139741265458944: stock 0x7f1804000b20
139741283514176: erase 1
139741283514176: ~Stock[0x55d46349f030] MS
139741265458944: Stock[0x7f1804000bd0] MS
139741265458944: stock2 0x7f1804000bd0
WARNING: stock != stock2
139741265458944: deleteStock[0x7f1804000bd0]
139741265458944: erase 1
139741265458944: ~Stock[0x7f1804000bd0] MS
139741265458944: deleteStock[0x7f1804000b20]
139741265458944: erase 0
139741265458944: ~Stock[0x7f1804000b20] MS

1.完整代码

//
// Created by root on 12/29/19.
//一个股票的内存池的迭代开发
//这个内存池对外提供一个get接口,可以通过这个接口获取给定名称的股票的动态内存
//

#if 1 //version 1

#include <boost/noncopyable.hpp>
#include <mutex>
#include <thread>
#include <memory>
#include <unordered_map>
#include <iostream>
#include <map>
#include <string>
#include <functional>

#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>

#define USE_BOOST_FUNC

using namespace std;


class Stock:boost::noncopyable{
public:
    Stock(string strName){
        strName_=strName;
        cout<<" Create Stock["<<strName_<<"]"<<endl;
    }
    ~Stock(){
        cout<<"Destroy Stock["<<strName_<<"]"<<endl;
    }
    string stockName() const { return strName_;}
private:
    string strName_;
};


//版本1中 股票池 std::map<string,shared_ptr<Stock>> stocks_ 中使用了shared_ptr<Stock>,
// 导致每只股票的引用计数无法清零,即股票对象在生命周期结束后无法被销毁
namespace Version1{
    //
    class StockFactory:boost::noncopyable{
    public:
        //如果要获取的股票不存在就创建一只这样股票并加入到哈希表中
        shared_ptr<Stock> get(const string& key);
        void dumpRefCount(){
            for(auto &item : stocks_){//如果是item会增加一次stocks_中的引用
                cout<<"Stock "<<item.first<<" use_count is "<<item.second.use_count()<<endl;
            }
        }
        size_t stockNum() const{ return stocks_.size();}
    private:
        mutable mutex mutex_;
        //StockFactory应该是一个单例,这里虽然没有用单例,但也禁用了copy,所以stocks_只有一份,多线程是会有竞争
        std::map<string,shared_ptr<Stock>> stocks_;
    };//StockFactory

      // 实现1
//    shared_ptr<Stock> StockFactory::get(const string &key) {
//        auto search = stocks_.find(key);
//        shared_ptr<Stock> pStock;
//
//        //加锁避免多线程竞争stock_
//        std::lock_guard<std::mutex> lock(mutex_);
//        //已经有这个元素了
//        if(search!=stocks_.end()){
//            pStock=stocks_[key];
//        }
//        else{//没有这个元素
//            pStock.reset(new Stock(key));
//            stocks_[key]=pStock;
//        }
//        return pStock;
//    }

    //更简单的实现
    shared_ptr<Stock> StockFactory::get(const string &key) {
        lock_guard<std::mutex> lock(mutex_);
        //从中取出一支股票的引用
        shared_ptr<Stock>& pStock=stocks_[key];
        //空指针说明没有这支股票
        if(nullptr==pStock){
            pStock.reset(new Stock(key));//重新创建一只股票
        }
        return pStock;
    }

    void test(){
        cout<<"hello version 1"<<endl;
        StockFactory stockFactory;
        {
            shared_ptr<Stock> stockHik = stockFactory.get("Hikvison");
            shared_ptr<Stock> stockAli = stockFactory.get("Alibaba");
            stockFactory.dumpRefCount();
            /* std::map<string,shared_ptr<Stock>> stocks_ 中用的是shared_ptr<Stock>所以可以看到
             * 最终两支股票的引用计数都是2
             * Stock Alibaba use_count is 2
             * Stock Hikvison use_count is 2
            */
        }
        stockFactory.dumpRefCount();
        /* std::map<string,shared_ptr<Stock>> stocks_ 中用的是shared_ptr<Stock>所以可以看到
         * 最终两支股票的引用计数都是1 无法被销毁
         * Stock Alibaba use_count is 1
         * Stock Hikvison use_count is 1
        */

    }
}//namespace version 1

//这个版本用weak_ptr替代了shared_ptr,解决了version 1 中股票对象无法销毁的问题,但这个版本中
// stocks_的大小只会增加不会减少,存在轻微的内存泄露,股票资源已经释放了,但仍然在stocks_中占据一个空间

namespace Version2{
    class StockFactory:boost::noncopyable{
    public:
        //如果要获取的股票不存在就创建一只这样股票并加入到哈希表中
        shared_ptr<Stock> get(const string& key);
        void dumpRefCount(){
            for(auto &item : stocks_){//如果是item会增加一次stocks_中的引用
                cout<<"Stock "<<item.first<<" use_count is "<<item.second.use_count()<<endl;
            }
            cout<<"size of stocks = "<<stockNum()<<endl;
        }
        size_t stockNum() const{ return stocks_.size();}
    private:
        mutable mutex mutex_;
        //StockFactory应该是一个单例,这里虽然没有用单例,但也禁用了copy,所以stocks_只有一份,多线程是会有竞争
        std::map<string,weak_ptr<Stock>> stocks_;
    };//StockFactory

    shared_ptr<Stock> StockFactory::get(const string &key) {
        shared_ptr<Stock> pStock;
        lock_guard<std::mutex> lock(mutex_);
        //从中取出一支股票的弱引用
        weak_ptr<Stock>& wkstock=stocks_[key];
        //去尝试提升这个弱引用
        pStock=wkstock.lock();
        if(nullptr==pStock){//提升失败,说明没有这支股票
            pStock.reset(new Stock(key));//重新创建一只股票
            wkstock=pStock;//更新原有的引用
        }
        return pStock;
    }

    void test(){
        cout<<"hello version 2"<<endl;
        StockFactory stockFactory;
        {
            shared_ptr<Stock> stockHik = stockFactory.get("Hikvison");
            shared_ptr<Stock> stockAli = stockFactory.get("Alibaba");

            stockFactory.dumpRefCount();
            /* std::map<string,weak_ptr<Stock>> stocks_ 中用的是weak_ptr<Stock>所以可以看到
             * 最终两支股票的引用计数都是1
             * Stock Alibaba use_count is 1
             * Stock Hikvison use_count is 1
             * size of stocks = 2
            */
        }
        stockFactory.dumpRefCount();
        /* 调用了股票的析构函数
         * Stock Alibaba use_count is 0
         * Stock Hikvison use_count is 0
         * size of stocks = 2
        */



    }
}//namespace version 2


//为了解决版本2中股票对象池数量只增不减的问题,这个版本采用了在reset方法中传入了自定义的删除函数,
// 保证对象在销毁时,从股票的哈希表中移除
//pStock.reset(new Stock(key),
//        std::bind(&StockFactory::deleteStock,this,std::placeholders::_1));
//但这个版本依然有问题,StockFactory::get 方法中的 pStock.reset方法把原始的this(StockFactory)
// 指针保存到了std::function(std::bind)中(StockFactory::deleteStock),如果StockFactory的生
// 命周期比stock短,那么stock的析构去回调StockFactory::deleteStock时,因为StockFactory已经析构,
// StockFactory 中的mutex_也被销毁了,这时执行到lock_guard<std::mutex> lock(mutex_);结果将是
// 未知的,最好的结果就是崩溃,也可能一直阻塞下去,test_bad()运行结果来看大概率一直阻塞下去.
namespace Version3{
    class StockFactory/*:boost::noncopyable*/{
    public:
        //如果要获取的股票不存在就创建一只这样股票并加入到哈希表中
        shared_ptr<Stock> get(const string& key);
        void dumpRefCount(){
            for(auto &item : stocks_){//如果是item会增加一次stocks_中的引用
                cout<<"Stock "<<item.first<<" use_count is "<<item.second.use_count()<<endl;
            }
            cout<<"size of stocks = "<<stockNum()<<endl;
        }
        StockFactory(const StockFactory& thr){
            cout<<"copy StockFactory"<<endl;
        }
        StockFactory(){
            cout<<"create StockFactory"<<endl;
        }
        ~StockFactory(){
            cout<<"delete StockFactory"<<endl;
        }
        size_t stockNum() const{ return stocks_.size();}
    private:
        mutable mutex mutex_;
        //StockFactory应该是一个单例,这里虽然没有用单例,但也禁用了copy,所以stocks_只有一份,多线程是会有竞争
        std::map<string,weak_ptr<Stock>> stocks_;
        void deleteStock(Stock* stock){
            if(stock){
                lock_guard<std::mutex> lock(mutex_);
                stocks_.erase(stock->stockName());
            }
            delete stock;
        }
    };//StockFactory

    shared_ptr<Stock> StockFactory::get(const string &key) {
        shared_ptr<Stock> pStock;
        lock_guard<std::mutex> lock(mutex_);
        //从中取出一支股票的弱引用
        weak_ptr<Stock>& wkstock=stocks_[key];
        //去尝试提升这个弱引用
        pStock=wkstock.lock();
        if(nullptr==pStock){//提升失败,说明没有这支股票
            pStock.reset(new Stock(key),
                    std::bind(&StockFactory::deleteStock,this,std::placeholders::_1));//重新创建一只股票
            wkstock=pStock;//更新原有的引用
        }
        return pStock;
    }

    void test(){
        cout<<"hello version 3"<<endl;
        StockFactory stockFactory;
        {
            shared_ptr<Stock> stockHik = stockFactory.get("Hikvison");
            shared_ptr<Stock> stockAli = stockFactory.get("Alibaba");

            stockFactory.dumpRefCount();
            /* std::map<string,weak_ptr<Stock>> stocks_ 中用的是weak_ptr<Stock>所以可以看到
             * 最终两支股票的引用计数都是1
             * Stock Alibaba use_count is 1
             * Stock Hikvison use_count is 1
             * size of stocks = 2
            */
        }
        stockFactory.dumpRefCount();
        /* 调用了股票的析构函数
         * Stock Alibaba use_count is 0
         * Stock Hikvison use_count is 0
         * size of stocks = 0
        */
    }
    void test_bad(){
        cout<<"hello version 3(bad test)"<<endl;

        shared_ptr<Stock> copy;
        {
            //用heap对象才能模拟出阻塞的问题,站对象不行(本来以为是因为栈对象会导致复制,
            // 可是也传递this指针时没有调用构造,可能是调用了拷贝构造了,测试发现拷贝构造也没有调用)
            shared_ptr<StockFactory> stockFactory(new StockFactory());
            {
                shared_ptr<Stock> stockHik = stockFactory->get("Hikvison");
                copy = stockHik;//延长hik的生命周期
                stockFactory->dumpRefCount();

//                    //模拟stockfactory的生命周期比stock短,这样模拟似乎不太好,毕竟要是手动删除的,version 4手动删除也没办法
//                    if(pStockFactory!= nullptr){
//                        delete pStockFactory;
//                    }
            }
        }

    }
}//namespace version 3

//将版本3中的this指针改为shared_ptr.方法是:采用 shared_from_this(),为了使用它StockFactory
// 不能是stack object 必须是heap object
// 到此为止,Stockfactory的已经没有问题了,但是Stockfactory的shared_ptr传入了deleteStock中,
// StockFactory的生命周期被意外延长了(事实上StockFactory应该是个单例,程序运行期间都不应该被销
// 毁,所以不存在生命周期意外延长的问题),当我们要在deleteStock中实现"如果对象还活着就调用其成员函数,
// 否则忽略之"这样的语义时,就会有问题了,因为deleteStock保存有Stockfactory的shared_ptr指针,
// Stockfactory会一直存在
namespace Version4{
    class StockFactory:public enable_shared_from_this<StockFactory>
            ,boost::noncopyable{
    public:
        //如果要获取的股票不存在就创建一只这样股票并加入到哈希表中
        shared_ptr<Stock> get(const string& key);
        void dumpRefCount(){
            for(auto &item : stocks_){//如果是item会增加一次stocks_中的引用
                cout<<"Stock "<<item.first<<" use_count is "<<item.second.use_count()<<endl;
            }
            cout<<"size of stocks = "<<stockNum()<<endl;
        }
        size_t stockNum() const{ return stocks_.size();}
    private:
        mutable mutex mutex_;
        //StockFactory应该是一个单例,这里虽然没有用单例,但也禁用了copy,所以stocks_只有一份,多线程是会有竞争
        std::map<string,weak_ptr<Stock>> stocks_;
        void deleteStock(Stock* stock){
            if(stock){
                lock_guard<std::mutex> lock(mutex_);
                stocks_.erase(stock->stockName());
            }
            delete stock;
        }
    };//StockFactory

    shared_ptr<Stock> StockFactory::get(const string &key) {
        shared_ptr<Stock> pStock;
        lock_guard<std::mutex> lock(mutex_);
        //从中取出一支股票的弱引用
        weak_ptr<Stock>& wkstock=stocks_[key];
        //去尝试提升这个弱引用
        pStock=wkstock.lock();
        if(nullptr==pStock){//提升失败,说明没有这支股票
            pStock.reset(new Stock(key),
                         std::bind(&StockFactory::deleteStock,
                                 shared_from_this(),
                                 std::placeholders::_1));//重新创建一只股票
            wkstock=pStock;//更新原有的引用
        }
        return pStock;
    }

    void test(){
        cout<<"hello version 4"<<endl;
        shared_ptr<Stock> copy;//
        {
            //用heap对象才能模拟出阻塞的问题,站对象不行(本来以为是因为栈对象会导致复制,
            // 可是也传递this指针时没有调用构造,可能是调用了拷贝构造了,测试发现拷贝构造也没有调用)
            shared_ptr<StockFactory> stockFactory(new StockFactory());
            {
                shared_ptr<Stock> stockHik = stockFactory->get("Hikvison");
                copy = stockHik;//延长hik的生命周期
                stockFactory->dumpRefCount();
            }
        }
        /* 调用了股票的析构函数
         * Stock Alibaba use_count is 0
         * Stock Hikvison use_count is 0
         * size of stocks = 0
        */
    }
}//namespace version 4

namespace Version5{
class StockFactory:public boost::enable_shared_from_this<StockFactory>
            ,boost::noncopyable{
    public:
        //如果要获取的股票不存在就创建一只这样股票并加入到哈希表中
        boost::shared_ptr<Stock> get(const string& key);
        void dumpRefCount(){
            for(auto &item : stocks_){//如果是item会增加一次stocks_中的引用
                cout<<"Stock "<<item.first<<" use_count is "<<item.second.use_count()<<endl;
            }
            cout<<"size of stocks = "<<stockNum()<<endl;
        }
        size_t stockNum() const{ return stocks_.size();}
    private:
        mutable mutex mutex_;
        //StockFactory应该是一个单例,这里虽然没有用单例,但也禁用了copy,所以stocks_只有一份,多线程是会有竞争
        std::map<string,boost::weak_ptr<Stock>> stocks_;
       static void weakDeleteStockCallback(const boost::weak_ptr<StockFactory>& wkFactory,Stock* stock){
           if(wkFactory.expired()){
               cout<<"factory died"<<endl;
           }else{
               boost::shared_ptr<StockFactory> factory(wkFactory);//尝试提升,提升失败为null
               if(factory!= nullptr)
               {
                   if(stock!= nullptr){
                       factory->removeStock(stock);
                       delete stock;
                   }
               }
               else{
                   cout<<"factory died"<<endl;
               }

           }

        }

        void removeStock(Stock* stock)
        {
            if (stock)
            {
                lock_guard<std::mutex> lock(mutex_);
                stocks_.erase(stock->stockName());

            }
        }


    };//StockFactory

    boost::shared_ptr<Stock> StockFactory::get(const string &key) {
        boost::shared_ptr<Stock> pStock;
        lock_guard<std::mutex> lock(mutex_);
        //从中取出一支股票的弱引用
        boost::weak_ptr<Stock>& wkstock=stocks_[key];
        //去尝试提升这个弱引用
        pStock=wkstock.lock();
        if(nullptr==pStock){//提升失败,说明没有这支股票
            pStock.reset(new Stock(key),
                         boost::bind(&StockFactory::weakDeleteStockCallback,
                                     boost::weak_ptr<StockFactory>(shared_from_this()),_1));
            wkstock=pStock;//更新原有的引用
        }
        return pStock;
    }

    void test_bad(){
        cout<<"hello version 5(test bad)"<<endl;

        boost::shared_ptr<Stock> copy;//
        {
            //用heap对象才能模拟出阻塞的问题,站对象不行(本来以为是因为栈对象会导致复制,
            // 可是也传递this指针时没有调用构造,可能是调用了拷贝构造了,测试发现拷贝构造也没有调用)
            boost::shared_ptr<StockFactory> stockFactory(new StockFactory());
            {
                boost::shared_ptr<Stock> stockHik = stockFactory->get("Hikvison");
                copy = stockHik;//延长hik的生命周期
                stockFactory->dumpRefCount();
            }
        }

        /* 调用了股票的析构函数
         * Stock Alibaba use_count is 0
         * Stock Hikvison use_count is 0
         * size of stocks = 0
        */
    }

    void test(){
        cout<<"hello version 5"<<endl;

        //用heap对象才能模拟出阻塞的问题,站对象不行(本来以为是因为栈对象会导致复制,
        // 可是也传递this指针时没有调用构造,可能是调用了拷贝构造了,测试发现拷贝构造也没有调用)
        boost::shared_ptr<StockFactory> stockFactory(new StockFactory());
        {
            boost::shared_ptr<Stock> stockHik = stockFactory->get("Hikvison");

            stockFactory->dumpRefCount();
        }

        /* 调用了股票的析构函数
         * Stock Alibaba use_count is 0
         * Stock Hikvison use_count is 0
         * size of stocks = 0
        */
    }
}//namespace version 5

#if 1 //boost和std的bind是一样的
namespace Version6{
    class StockFactory:public std::enable_shared_from_this<StockFactory>
            ,boost::noncopyable{
    public:
        //如果要获取的股票不存在就创建一只这样股票并加入到哈希表中
        std::shared_ptr<Stock> get(const string& key);
        void dumpRefCount(){
            for(auto &item : stocks_){//如果是item会增加一次stocks_中的引用
                cout<<"Stock "<<item.first<<" use_count is "<<item.second.use_count()<<endl;
            }
            cout<<"size of stocks = "<<stockNum()<<endl;
        }
        size_t stockNum() const{ return stocks_.size();}
    private:
        mutable mutex mutex_;
        //StockFactory应该是一个单例,这里虽然没有用单例,但也禁用了copy,所以stocks_只有一份,多线程是会有竞争
        std::map<string,std::weak_ptr<Stock>> stocks_;

        //不是static才能编译过
        static void weakDeleteStockCallback(const std::weak_ptr<StockFactory>& wkFactory,Stock* stock){
            if(wkFactory.expired()){//如果factory的生命周期比stock短就结束,陈硕书上的源码是没有这个判断的
                cout<<"factory died"<<endl;
            }
            else{
                std::shared_ptr<StockFactory> factory(wkFactory);//尝试提升,提升失败为null
                if(factory!= nullptr)
                {
                    if(stock!= nullptr){
                        factory->removeStock(stock);
                        delete stock;
                    }
                }
                else{
                    cout<<"factory died"<<endl;
                }

            }

        }

        void removeStock(Stock* stock)
        {
            if (stock)
            {
                lock_guard<std::mutex> lock(mutex_);
                stocks_.erase(stock->stockName());

            }
        }

    };//StockFactory

    std::shared_ptr<Stock> StockFactory::get(const string &key) {
        std::shared_ptr<Stock> pStock;
        lock_guard<std::mutex> lock(mutex_);
        //从中取出一支股票的弱引用
        std::weak_ptr<Stock>& wkstock=stocks_[key];
        //去尝试提升这个弱引用
        pStock=wkstock.lock();
        if(nullptr==pStock){//提升失败,说明没有这支股票
            pStock.reset(new Stock(key),
                         std::bind(&StockFactory::weakDeleteStockCallback,
                                   std::weak_ptr<StockFactory>(shared_from_this()),
                                     std::placeholders::_1));
            wkstock=pStock;//更新原有的引用
        }
        return pStock;
    }

    void test(){
        cout<<"hello version 6"<<endl;

        {
            //用heap对象才能模拟出阻塞的问题,站对象不行(本来以为是因为栈对象会导致复制,
            // 可是也传递this指针时没有调用构造,可能是调用了拷贝构造了,测试发现拷贝构造也没有调用)
            shared_ptr<StockFactory> stockFactory(new StockFactory());
            {
                std::shared_ptr<Stock> stockHik = stockFactory->get("Hikvison");
                stockFactory->dumpRefCount();
            }
        }

        /* 调用了股票的析构函数
         * Stock Alibaba use_count is 0
         * Stock Hikvison use_count is 0
         * size of stocks = 0
        */
    }

    void test_bad(){
        cout<<"hello version 6(test bad)"<<endl;

        std::shared_ptr<Stock> copy;//
        {
            //用heap对象才能模拟出阻塞的问题,站对象不行(本来以为是因为栈对象会导致复制,
            // 可是也传递this指针时没有调用构造,可能是调用了拷贝构造了,测试发现拷贝构造也没有调用)
            std::shared_ptr<StockFactory> stockFactory(new StockFactory());
            {
                std::shared_ptr<Stock> stockHik = stockFactory->get("Hikvison");
                copy = stockHik;//延长hik的生命周期
                stockFactory->dumpRefCount();
            }
        }

        /* 调用了股票的析构函数
         * Stock Alibaba use_count is 0
         * Stock Hikvison use_count is 0
         * size of stocks = 0
        */
    }

}//namespace version 6
#endif


#if 1 //boost和std的bind是一样的
/*
         void removeStock(Stock* stock)
        {
            if (stock)
            {
                lock_guard<std::mutex> lock(mutex_);
                stocks_.erase(stock->stockName());

            }
        }
        多线程情况下这里会有竞争,Race condition 发生在 StockFactory::deleteStock()
        这个成员函数里,如果进入 deleteStock 之后,在 lock 之前,有别的线程调用了相同
        key 的 StockFactory::get(),会造成此 key 被从 stocks_ 哈希表中错误地删除,
        因此会重复创建 Stock 对象。程序不会 crash 也不会有 memory leak,但是程序中
        存在两个相同 key 的 Stock 对象,违背了对象池应有的语意(同一对象的内存应该相同)。

        详细解释参考:https://zhuanlan.zhihu.com/p/30522095
*/
namespace Version7{
    class StockFactory:public std::enable_shared_from_this<StockFactory>
            ,boost::noncopyable{
    public:
        //如果要获取的股票不存在就创建一只这样股票并加入到哈希表中
        std::shared_ptr<Stock> get(const string& key);
        void dumpRefCount(){
            for(auto &item : stocks_){//如果是item会增加一次stocks_中的引用
                cout<<"Stock "<<item.first<<" use_count is "<<item.second.use_count()<<endl;
            }
            cout<<"size of stocks = "<<stockNum()<<endl;
        }
        size_t stockNum() const{ return stocks_.size();}
    private:
        mutable mutex mutex_;
        //StockFactory应该是一个单例,这里虽然没有用单例,但也禁用了copy,所以stocks_只有一份,多线程是会有竞争
        std::map<string,std::weak_ptr<Stock>> stocks_;

        //不是static才能编译过
        static void weakDeleteStockCallback(const std::weak_ptr<StockFactory>& wkFactory,Stock* stock){
            if(wkFactory.expired()){//如果factory的生命周期比stock短就结束,陈硕书上的源码是没有这个判断的
                cout<<"factory died"<<endl;
            }
            else{
                std::shared_ptr<StockFactory> factory(wkFactory);//尝试提升,提升失败为null
                if(factory!= nullptr)
                {
                    if(stock!= nullptr){
                        factory->removeStock(stock);
                        delete stock;
                    }
                }
                else{
                    cout<<"factory died"<<endl;
                }

            }

        }

//        void removeStock(Stock* stock)
//        {
//            if (stock)
//            {
//                lock_guard<std::mutex> lock(mutex_);
//                stocks_.erase(stock->stockName());
//
//            }
//        }

        void removeStock(Stock* stock)
        {
            if (stock)
            {
                std::lock_guard<std::mutex> lock(mutex_);
                auto it = stocks_.find(stock->stockName());
                assert(it != stocks_.end());
                if (it->second.expired())
                {
                    stocks_.erase(stock->stockName());
                }
            }
        }


    };//StockFactory

    std::shared_ptr<Stock> StockFactory::get(const string &key) {
        std::shared_ptr<Stock> pStock;
        lock_guard<std::mutex> lock(mutex_);
        //从中取出一支股票的弱引用
        std::weak_ptr<Stock>& wkstock=stocks_[key];
        //去尝试提升这个弱引用
        pStock=wkstock.lock();
        if(nullptr==pStock){//提升失败,说明没有这支股票
            pStock.reset(new Stock(key),
                         std::bind(&StockFactory::weakDeleteStockCallback,
                                   std::weak_ptr<StockFactory>(shared_from_this()),
                                   std::placeholders::_1));
            wkstock=pStock;//更新原有的引用
        }
        return pStock;
    }

    void test(){
        cout<<"hello version 7"<<endl;

        {
            //用heap对象才能模拟出阻塞的问题,站对象不行(本来以为是因为栈对象会导致复制,
            // 可是也传递this指针时没有调用构造,可能是调用了拷贝构造了,测试发现拷贝构造也没有调用)
            shared_ptr<StockFactory> stockFactory(new StockFactory());
            {
                std::shared_ptr<Stock> stockHik = stockFactory->get("Hikvison");
                stockFactory->dumpRefCount();
            }
            stockFactory->dumpRefCount();
        }

        /* 调用了股票的析构函数
         * Stock Alibaba use_count is 0
         * Stock Hikvison use_count is 0
         * size of stocks = 0
        */
    }

    void test_bad(){
        cout<<"hello version 7(test bad)"<<endl;

        std::shared_ptr<Stock> copy;//
        {
            //用heap对象才能模拟出阻塞的问题,站对象不行(本来以为是因为栈对象会导致复制,
            // 可是也传递this指针时没有调用构造,可能是调用了拷贝构造了,测试发现拷贝构造也没有调用)
            std::shared_ptr<StockFactory> stockFactory(new StockFactory());
            {
                std::shared_ptr<Stock> stockHik = stockFactory->get("Hikvison");
                copy = stockHik;//延长hik的生命周期
                stockFactory->dumpRefCount();
            }
            stockFactory->dumpRefCount();
        }
        
    }

}//namespace version 7
#endif

int main()
{

//    Version1::test();
//    Version2::test();
//    Version3::test();
//    Version3::test_bad();
//    Version4::test();
//    Version5::test();

//    Version5::test();
//    Version5::test_bad();

//    Version6::test();
//    Version6::test_bad();

    Version7::test();
    Version7::test_bad();

    getchar();
    return 0;
}

#endif
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值