c.vim


        
       
5.构造和析构的线程安全
    (1).构造函数的线程安全
        规则:不要在构造函数中注册任何回调函数;
              不要在构造函数中把this传递给跨越线程的对象。
              使用二段式构造(构造函数+Initialize函数)
             
        ex:这里有问题的原因是,可能在构造函数没有执行完成之前,Obs就去调用类A的函数了。
        class A
        {
            public:
                A(Obs *pObs)
                {
                    pObs->Obeserve(this);
                }
        };
       
        ex:解决方法是二段式构造
        class A
        {
            public:
                A(Obs *pObs)
                {
                    ...
                }
       
                Initialize()
                {
                    //在Initialize里面干些线程安全的事情
                }
        };
   
   
   
   
    (2).析构的线程安全
        .问题的提出:
           当一个对象被多个线程访问的时候,
           如下代码可能有两个问题:一个线程可能正在访问一个被释放了的变量pX。
                                   而且mutex也是一个函数成员变量,在~A()析构函数执行了之后mutex就被释放了。
          
           ex:
           先线程1中执行了下面的代码:
           ~A()
           {
              mutex_lock();
              delete pX;
              ...
           }
          
           但是另外一个线程2,却要访问变量 px:
           void A::getX()
           {
              mutex_lock();
              pX->...
              mutex_unlock();
           }
          
 
 
2013年11月12日:
1.observer模式的线程安全
    (1).observer的一般模式是:
        class  Observer                  
        {                                
        public:                          
            virtual ~Observer()          
            {                            
            }                            
            virtual void update() =0 ;   
        };                               
        class Observable                 
        {                                
        public:                          
            void register(Observer*   x);
            void unregister(Observer* x);
            void notifyObservers()       
            {                            
                foreach Observer*  x     
                {                        
                    // 这行是伪代码      
                    x->update(); //(3)   
                                         
                }                        
            } 
           
            ~Observable()
            {
             unregister 同时delete 所有的observer指针
            }                          
            //                           
            ...                          
        }  
       
    (2).分析多线程中observer模式可能出现的问题
        在标注(3)(x->update)上有可能出现的问题是:如何得知 observer对象 x 还活着? 因为有可能线程A在执行 ~Observable()的时候,
        线程B已经进入到 x->update()里面, 这个问题就非常危险了。
       
        也行你会这样修改observer:但是这里仍然有问题,如何确保_observable对象存在?并且如何确保 在执行标注(3)的同时,没有
        另外一个线程在执行标注(4);
        class  Observer                    
        {                                  
        public:                                                       
            virtual ~Observer()            
            {                              
                _observable->unregister(...) //(4)
            }                              
            virtual void update() =0 ;     
                                   
            Observable *_observable ;      
        };                               
       
        上面的问题让你焦头烂额??
        也行你会用 线程的同步来解决这些问题。但是这很有可能让你的线程失去并发特性,而变成按照一定顺序来执行线程了。这
        对程序性能明显有很大的影响。
       
        怎样解决??
        通过分析上面的问题,我的总结这里的场景可以归结为:一个线程在调用对象的功能,但是很有可能同时有另外一个线程在
        调用同一个对象的析构函数或者这个析构函数已经执行完成,另一个线程却不知。
       
       
1.1怎样解决上面场景中出现的问题?
    我们的解决方法:使用weak_ptr和shared_ptr
   
    ex:
                                                                                                  
    class Observable    //not100% thread safe!                                                    
    {                                                                                                 
    public:                                                                                           
        void  register(weak_ptr<Observer> x);                                                     
        void  unregister(weak_ptr<Observer> x);                                                   
        //可用std::remove/vector::erase实现                                                       
        void  notifyObservers()                                                                   
        {                                                                                         
            MutexLock lock(mutex_);                                                               
            Iterator it= observers_.begin();                                                      
            while(it!=observers_.end())                                                           
            {                                                                                     
                shared_ptr<Observer> obj(it->lock());//尝试提升,这一步是线程安全的               
                    if(obj)                                                                       
                    {                                                                             
                        //提升成功,现在引用计数值至少为2(想想为什么?)                         
                            obj->update();//没有竞态条件,因为obj在栈上,对象不可能在本作用域内销毁
                            ++it;                                                                 
                    }                                                                             
                    else                                                                          
                    {                                                                             
                        //对象已经销毁,从容器中拿掉                                              
                        weak_ptr it=observers_.erase(it);                                         
                    }                                                                             
            }                                                                                     
        }                                                                                         
    private:                                                                                      
        std::vector<weak_ptr<Observer>>  observers_;//(5)                                                                          
        mutable Mutex mutex_;                                                                     
    };              
   
    思考:如果把(5)处改为vector<shared_ptr<Observer>>observers_;,会有什么后果? 后果就是 Observer 永远活着,知道调用 vector的析构
    函数。但是我们这里用weak_ptr,是因为我们保证对象在某个地方活着。
   
    需要注意循环引用:通常是 owner持有A的shared_ptr, A 持有 owner的 weak_ptr.
   
2.RAII
    RAII(Resource Acquisition Is Initialization),中文的翻译是:资源获取就是初始化。
   
    背景:系统的资源是有限的,怎样正确的做到资源的使用和释放就是非常重要的编程方法。
    ex:
    void A::f()
    {
       FILE *pFile = fopen(...);
      
       ...
      
       thr();
      
       if(..)
       {
           return ...;
       }
      
       fclose(pFile);
    }
   
    上面的代码是肯定有问题的,因为如果直接就return的话,文件就没有关闭。但是如果我们中间有很多的if语句,
    并且thr()还有可能会跑出异常,那么我们怎样正确的 fclose(pFile) 呢?
   
    如果我们到处添加 fclose(pFile),那么程序就会很难管理。
   
    解决方法1: 在类 A 的析构函数中调用 fclose(pFile)
   
    解决方法2:  使用shared_ptr,或者scoped_ptr 能很好的解决问题。
   
    RAII就是解决 资源关闭的问题的。
    这种思想本质是在析构函数中处理。因为当函数返回的时候会调用析构函数;并且当抛出异常的时候,系统会沿着调用栈,
    寻找catch子句,这个过程stack unwinding。c++规定,在stack unwinding这个过程中,系统必须确保调用所有以创建的局部对象
    的析构函数。
                                  
                                  
2.原子锁的利用
  我们有时需要在创建一个类的第一个对象的时候,做某种初始化,并且只初始化一次。
  在调用这个类的最后一个对象的析构函数的时候,做某种析构动作,而且只析构一次。
  这里就到了 interlockadd大显身手的时候了:
  ex:
  class A                                                                                 
  {                                            
      A()                                      
      {                                        
          InterlockedInc64(m_instanceNum);     
                                             
          if (m_instanceNum==1)                
          {                                    
              dosomething...                   
          }                                    
      }                                        
                                             
      ~A()                                     
      {                                        
          InterlockedDecrement64(m_instanceNum);
          if (m_instanceNum==1)                
          {                                    
              dosomething...                   
          }                                    
      }                                        
                                             
      long m_instanceNum;                      
  };                 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值