C++ 中线程安全的对象生命期管理概要二

9 篇文章 1 订阅

《Linux多线程服务端编程》的1.11节以对象池为例子讲解了shared_ptr技术与陷阱。



假设有 Stock 类,代表一只股票的价格。每一只股票有一个惟一的字符串标识,比如 Google 的 key 是 "NASDAQ:GOOG",IBM 是 "NYSE:IBM"。Stock 对象是个主动对象,它能不断获取新价格。为了节省系统资源,同一个程序里边每一只出现的股票只有一个 Stock 对象,如果多处用到同一只股票,那么 Stock 对象应该被共享。如果某一只股票没有再在任何地方用到,其对应的 Stock 对象应该析构,以释放资源,这隐含了“引用计数”。

version1的代码如下:

class StockFactory : boost::noncopyable

{  // questionable code
 public:
  shared_ptr<Stock> get(const string& key);

 private:
  std::map<string, shared_ptr<Stock> > stocks_;
  mutable Mutex mutex_;
};
version1存在的问题:stocks_里存的是shared_ptr,始终有“铁丝”绑着。
version1的解决办法: 改成weak_ptr,如下的version2:
class StockFactory : boost::noncopyable
{
 public:
  shared_ptr<Stock> get(const string& key)
  {
    shared_ptr<Stock> pStock;
    MutexLock lock(mutex_);
    weak_ptr<Stock>& wkStock = stocks_[key];  // 如果 key 不存在,会默认构造一个
    pStock = wkStock.lock();  // 尝试把棉线提升为铁丝
    if (!pStock) {
      pStock.reset(new Stock(key));
      wkStock = pStock;  // 这里更新了 stocks_[key],注意 wkStock 是个引用
    }
    return pStock;
  }


 private:
  std::map<string, weak_ptr<Stock> > stocks_;
  mutable Mutex mutex_;
};
version2存在的问题: stocks_ 的大小只增不减,stocks_.size() 是曾经存活过的 Stock 对象的总数,即便活的 Stock 对象数目降为 0,出现了轻微的内存泄漏。
version2的解决办法:利用 shared_ptr 的定制析构功能。shared_ptr 的构造函数可以有一个额外的模板类型参数,传入一个函数指针或仿函数 d,在析构对象时执行 d(p),进一步改成version3:
class StockFactory : boost::noncopyable
{
public:
  shared_ptr<Stock> get(const string& key)
  {
    shared_ptr<Stock> pStock;
    MutexLock lock(mutex_);
    weak_ptr<Stock>& wkStock = stocks_[key];  // 如果 key 不存在,会默认构造一个
    pStock = wkStock.lock();  // 尝试把棉线提升为铁丝
    if (!pStock) {
      pStock.reset(new Stock(key),boost::bind(&StockFactory::deleteStock, this, _1));  
      wkStock = pStock;  // 这里更新了 stocks_[key],注意 wkStock 是个引用
    }
    return pStock;
  }       

 private:
  void deleteStock(Stock* stock)
  {
    if (stock) {
      MutexLock lock(mutex_);
      stocks_.erase(stock->key());
    }
    delete stock;  // sorry, I lied
  }

version3存在的问题:StockFactory::get() 把原始指针 this 保存到了 boost::function 中 (6),如果 StockFactory 的生命期比 Stock 短,那么 Stock 析构时去回调 StockFactory::deleteStock 就会 core dump。

version3的解决办法:用 enable_shared_from_this。这是一个以其派生类为模版类型实参的基类模版,继承它,this 就能变身为 shared_ptr。修改成如下的 version4:

class StockFactory : public boost::enable_shared_from_this<StockFactory>,
                       boost::noncopyable
 {
 public:
     shared_ptr<Stock> get(const string& key)
    {
        shared_ptr<Stock> pStock;
        MutexLock lock(mutex_);
        weak_ptr<Stock>& wkStock = stocks_[key];  // 如果 key 不存在,会默认构造一个
        pStock = wkStock.lock();  // 尝试把棉线提升为铁丝
        if (!pStock) {
              pStock.reset(new Stock(key),boost::bind(&StockFactory::deleteStock, shared_from_this, _1));  
              wkStock = pStock;  // 这里更新了 stocks_[key],注意 wkStock 是个引用
        }
        return pStock;
    }   

 }

version4存在的问题:StockFactory 的生命期似乎被意外延长了。
version4的解决办法:利用 weak_ptr,我们可以把 weak_ptr 绑到 boost::function 里,这样对象的生命期就不会被延长,然后在回调的时候先尝试提升为 shared_ptr,如果提升成功,说明接受回调的对象还健在,那么就执行回调;如果提升失败,就不必劳神了。
最终版的代码:
class StockFactory : public boost::enable_shared_from_this<StockFactory>,  
                       boost::noncopyable  
{  
 public:  
  shared_ptr<Stock> get(const string& key)  
  {  
    shared_ptr<Stock> pStock;  
    MutexLock lock(mutex_);  
    weak_ptr<Stock>& wkStock = stocks_[key];  
    pStock = wkStock.lock();  
    if (!pStock) {  
      pStock.reset(new Stock(key),  
                   boost::bind(&StockFactory::weakDeleteCallback,  
                               boost::weak_ptr<StockFactory>(shared_from_this()),  
                               _1));  
      // 上面必须强制把 shared_from_this() 转型为 weak_ptr,才不会延长生命期  
       wkStock = pStock;  
    }  
    return pStock;  
  }  
private:  
  static void weakDeleteCallback(boost::weak_ptr<StockFactory> wkFactory,  
                                 Stock* stock)  
  {  
    shared_ptr<StockFactory> factory(wkFactory.lock());  
    if (factory) {  // 如果 factory 还在,那就清理 stocks_  
      factory->removeStock(stock);  
    }  
    delete stock;  // sorry, I lied  
  }  
  void removeStock(Stock* stock)   
  {  
    if (stock) {  
      MutexLock lock(mutex_);  
      stocks_.erase(stock->key());  
    }  
  }  
 private:  
  std::map<string, weak_ptr<Stock> > stocks_;  
  mutable Mutex mutex_;  
};  





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

X-Programer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值