muduo之当析构函数遇见线程安全

一、当析构函数遇到多线程

当一个对象能被多个线程同时看到时,那么对象的销毁时机就会变得模糊不清,可能出现多种竞态条件:
① 在即将析构一个对象时,如何知道此时是否有别的线程正在执行该对象的成员函数?
② 如何保证在执行成员函数期间,对象不会被另一个线程析构?
③ 在调用某个对象的成员函数之前,如何得知该对象还活着?它的析构函数会不会碰巧执行到一半?

二、对象的销毁太难

2.1 作为数据成员的mutex不能保护析构

mutex只能保证函数一个接着一个地执行。下面从代码开始分析:
在这里插入图片描述
尽管线程A在销毁对象x之后把x设为NULL,尽管线程B在调用x之前检查了指针x地值,但是还是无法避免一种race condition:
1.线程A调用delete x,执行到(1)free internal state,线程A此刻已经持有了mutex_,即将继续向下执行。
2.x并没有被线程A设为NULL,因此线程B能通过if(x),进入x->update(),将阻塞在(2)处
3.之后因为析构函数销毁x地同时也将成员变量mutex_给销毁,因此(2)将会发生难以预料的结果(可能会永远阻塞在(2)处,也可能会进入“临界区”,然后core dump,或者发生更糟糕的情况)
结论:作为class数据成员的mutex_只能同步本class的其他数据成员的读和写,不能保护安全的析构。

2.2 线程不安全的Observer模式
class Subject
{
public:
  virtual ~Subject() {};
  void attach(Observer* obsvr) 
  {
    m_observers.push_back(obsvr);
  }
  void remove(Observer* obsvr)
  {
    m_observers.remove(obsvr);
  }
  void notify(const std::string &msg)
  {
    for(Observer* obs:m_observers) 
      obs->Update();  [1]
  }
private: 
  std::vector<Observer* > m_observers; //观察者集合
};

线程A:当Subject通知每一个Observer时(代码[1]),它无法得知Observer对象是否还活着?
线程B:观察者Observer正在调用析构函数
此时线程A中,该Observer对象处于将死未死的状态,调用Update()函数,core dump恐怕是最幸运的结果。

三、一个万能的解决方案

上文讲解了各种各样的错误,解决方案的核心要点是:析构过程不需要保护,所有人都用不到的东西一定是垃圾,垃圾自动回收的原理是“引用计数为0时,该对象就是垃圾,需要销毁”,引出了带引用计数的智能指针。

举例:下面使用weak_ptr / share_ptr + Mutex实现线程安全的Observer模式
既然通过weak_ptr的expired函数可以探查对象的生死,那么Observer的竞态问题就很容易解决,只要让Observer保存weak_ptr<Observer>即可。

class Subject
{
public:
  virtual ~Subject() {};
  void attach(weak_ptr<Observer> obsvr);
  // void remove(weak_ptr<Observer> obsvr); //不需要它,会自动移除
  void notify(const std::string &msg)
  {
    //加锁,任何一个时刻,保证只有一个线程去唤醒所有Observer
    MutexLockGuard lock(mutex_); 
    std::vector<weak_ptr<Observer>>::iterator iter = m_observers.begin();
    for(iter != m_observers.end())
    {
      //尝试提升,即获得weak_ptr对应的shared_ptr
      shared_ptr<Observer> obj(iter->lock()); 
      if(obj) //成功,返回一个非空的shared_ptr
      {
        //提升成功,现在的引用计数至少为2(想想为什么?)
        obj->Opdate(); //没有竞态条件
        ++iter;
      }
      else //失败,返回一个空的shared_ptr
      {
        //对象已经销毁,从容器中拿掉weak_ptr
        it = m_observers.erase(iter);
      }     
    }
  }
private: 
  mutable MutexLock mutex_;
  std::vector<weak_ptr<Observer>> m_observers; //观察者集合
};

分析:在调用Update()之前,先用it->lock()判断该对象是否存在,如果存在就调用Update();如果不存在,就删除it。
总的说就是,使用weak_ptr可以探查对象的生死。以便控制对象的生命周期。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值