c++库

线程安全的对象生命周期2019-10-28

2019-10-30  本文已影响0人  雪上霜

1、当析构遇到多线程

        1)即将析构的一个对象是否有其他的线程正在执行该对象的成员函数

        2)该对象成员函数执行期间,对象会不会另一个线程析构

        3)在调用成员函数之前,如何知道该对象还活着,析构会不会执行到一半?

2、线程安全定义

        1)多个线程同时访问,表现出正确的行为

        2)无论系统如何调用,无论线程如何交织

        3)调用端代码无序额外的同步或其他协调动作

// A thread-safe counter

class Counter : boost::noncopyable

{

  // copy-ctor and assignment should be private by default for a class.

public:

  Counter() : value_(0) {}

  Counter& operator=(const Counter& rhs);

  int64_t value() const;

  int64_t getAndIncrease();

  friend void swap(Counter& a, Counter& b);

private:

  mutable MutexLock mutex_;

  int64_t value_;

};

int64_t Counter::value() const

{

  MutexLockGuard lock(mutex_);//lock 的析构会晚于返回对象的构造,

  return value_;                            //因此有效地保护了这个共享数据。

}

int64_t Counter::getAndIncrease()

{

  MutexLockGuard lock(mutex_);

  int64_t ret = value_++;

  return ret;

}

//当然在实际项目中,这个class用原子操作更合理,这里用锁仅仅为了举例。

3、对象构造要线程安全,唯一要求的是构造期间不要泄露this指针

        1)不要在构造函数中注册任何回调;

        2)不要在构造函数中把this传给跨线程的对象;

        3)即便构造函数最后一行也不行。

//不要这么做

class Foo : public Observer

{

public:

            Foo(Observable* s)

            {

                s->register_(this);    //非线程安全

            }

        virtual void update();

};

class Foo : public Observer

{

public:

        Foo(){}

        virtual void update();

        //在另一个函数中构造之后执行回调

        void observe(Observable* s)

        {

                s->register_(this);

        }

};

Foo* pFoo = new Foo;

Observable* s = getSubject();

pFoo->observe(s);//二段式构造  或者直接写s->register_(pFoo);

野指针:未经初始化的指针。

空悬指针:指向已经销毁的对象或已经回收的地址。

成员函数用来保护临界区的互斥器本身必须是有效地,而析构函数破坏了这一假设,它把mutex成员变量销毁掉。----以下代码解释此句。

Foo::~Foo()

{

    MutexLockGuard lock(mutex_);

    // free internal state (1)

}

void Foo::update()

{

    MutexLockGuard lock(mutex_);    //(2)

    //make use of internal state

}

此时A、B两个线程同时对对象x操作。

extern Foo* x;

//thread A

delete x;

x = NULL;

//thread B

if(x)

{

    x->update();

}

1、线程A执行到了析构函数(1)处,已经持有了互斥锁,即将继续执行下去。

2、线程B通过了if(x)检测,阻塞在(2)处。

因为析构函数会把mutex_销毁掉。

面向对象程序设计中,对象三种关系:composition(组合/复合)、aggregation(关联/联系)、association(聚合)。

c++标准对构造函数和析构函数中调用虚函数的行为有明确规定,但是没有考虑并发调用的情况。

线程安全的对象生命周期2019-10-28

share_ptr控制对象的生命周期,share_ptr是强引用,只要有一个指向x的对象share_ptr存在,该x对象就不会析构,当指向对象x的最后一个share_ptr析构或reset()的时候,x保证会被销毁。

weak_ptr不会控制对象的生命周期,但是它知道对象是否还活着,如果对象还活着,可以提升为有效的share_ptr;如果对象死了,提升会失败,返回一个空的share_ptr。“提升/lock()”行为时线程安全的。

share_ptr/weak_ptr的“计数”在主流平台是原子操作,没有用锁,性能不俗。

share_ptr/weak_ptr的线程安全级别与std::string和STL容器一样。

c++可能出现的内存问题:

1、缓冲区溢出:用vector<char>/string或自己编写Buffer class来管理缓冲区,自动记住用缓冲区的长度,并通过成员函数而不是裸指针来修改缓冲区。

2、空悬指针/野指针:用share_ptr/weak_ptr

3、充分释放:用scoped_ptr,只在对象析构的时候释放一次。

4、内存泄漏:用scoped_tr,对象析构的时候自动释放内存。

5、不配对的new[]/delete:把new[]统统替换为vector/scoped_array。

线程安全的对象生命周期2019-10-28

class Observable

{

 public:

  void register_(boost::weak_ptr<Observer> x);//参数类型可用const weak_ptr<Observer>&

  // void unregister(boost::weak_ptr<Observer> x);

  void notifyObservers()

  {

    muduo::MutexLockGuard lock(mutex_);

    Iterator it = observers_.begin();

    while (it != observers_.end())

    {

      boost::shared_ptr<Observer> obj(it->lock());//尝试提升,这一步是线程安全的。

      if (obj)

      {

        //提升成功,现在引用计数值至少为2

        obj->update();//没有竟态条件,因为obj在站上,对象不肯在本作用域内销毁。

        ++it;

      }

      else

      {

        printf("notifyObservers() erase\n");

        it = observers_.erase(it);    //对象已经销毁,从容器中拿掉weak_ptr

      }

    }

  }

 private:

  mutable muduo::MutexLock mutex_;

  std::vector<boost::weak_ptr<Observer> > observers_;

  typedef std::vector<boost::weak_ptr<Observer> >::iterator Iterator;

};

一个share_ptr对象实体可以被多个线程同时读取;

两个share_ptr对象实体可以被两个线程同时写入,“析构”算写操作;

如果要从多个线程读写同一个share_ptr对象,那么需要加锁。

在多个线程中同时访问同一个share_ptr正确用mutex保护做法:

MutexLock mutex;

share_ptr<Foo> globalPtr;

//我们的任务时把globalPTR安全的穿给doit();

void doit(const shared_ptr<Foo>& pFoo);

为了拷贝globalPtr,需要在读取它的时候加锁,

void read()

{

    shared_ptr<Foo> localPtr;

    {

        MutexLockGuard lock(mutex);

        localPtr = globalPtr;    //read globalPtr

    }

    //use localPtr since here, 读写localPtr也无须加锁。

    doit(localPtr);

  }

//写时需要加锁

void write()

{

shared_ptr<Foo>  newPtr(new Foo);        //对象的创建在临界区之外

    {

MutexLockGuard lock(mutex);

globalPtr = newPtr;    //write to  globalPtr

    }

//usenewPtr since here, 读写newPtr也无须加锁。

doit(newPtr);

  }

namespace version1

{

// questionable code

class StockFactory : boost::noncopyable

{

 public:

  boost::shared_ptr<Stock> get(const string& key)

  {

    muduo::MutexLockGuard lock(mutex_);

    boost::shared_ptr<Stock>& pStock = stocks_[key];

    if (!pStock)

    {

      pStock.reset(new Stock(key));

    }

    return pStock;

  }

 private:

  mutable muduo::MutexLock mutex_;

  std::map<string, boost::shared_ptr<Stock> > stocks_;

};

}

namespace version2

{

class StockFactory : boost::noncopyable

{

 public:

  boost::shared_ptr<Stock> get(const string& key)

  {

    boost::shared_ptr<Stock> pStock;

    muduo::MutexLockGuard lock(mutex_);

    boost::weak_ptr<Stock>& wkStock = stocks_[key];

    pStock = wkStock.lock();

    if (!pStock)

    {

      pStock.reset(new Stock(key));

      wkStock = pStock;

    }

    return pStock;

  }

 private:

  mutable muduo::MutexLock mutex_;

  std::map<string, boost::weak_ptr<Stock> > stocks_;

};

}

namespace version3

{

class StockFactory : boost::noncopyable

{

 public:

  boost::shared_ptr<Stock> get(const string& key)

  {

    boost::shared_ptr<Stock> pStock;

    muduo::MutexLockGuard lock(mutex_);

    boost::weak_ptr<Stock>& wkStock = stocks_[key];

    pStock = wkStock.lock();

    if (!pStock)

    {

      pStock.reset(new Stock(key),

                   boost::bind(&StockFactory::deleteStock, this, _1));

      wkStock = pStock;

    }

    return pStock;

  }

 private:

  void deleteStock(Stock* stock)

  {

    printf("deleteStock[%p]\n", stock);

    if (stock)

    {

      muduo::MutexLockGuard lock(mutex_);

      stocks_.erase(stock->key());  // This is wrong, see removeStock below for correct implementation.

    }

    delete stock;  // sorry, I lied

  }

  mutable muduo::MutexLock mutex_;

  std::map<string, boost::weak_ptr<Stock> > stocks_;

};

}

namespace version4

{

class StockFactory : public boost::enable_shared_from_this<StockFactory>,

                     boost::noncopyable

{

 public:

  boost::shared_ptr<Stock> get(const string& key)

  {

    boost::shared_ptr<Stock> pStock;

    muduo::MutexLockGuard lock(mutex_);

    boost::weak_ptr<Stock>& wkStock = stocks_[key];

    pStock = wkStock.lock();

    if (!pStock)

    {

      pStock.reset(new Stock(key),

                   boost::bind(&StockFactory::deleteStock,

                               shared_from_this(),

                               _1));

      wkStock = pStock;

    }

    return pStock;

  }

 private:

  void deleteStock(Stock* stock)

  {

    printf("deleteStock[%p]\n", stock);

    if (stock)

    {

      muduo::MutexLockGuard lock(mutex_);

      stocks_.erase(stock->key());  // This is wrong, see removeStock below for correct implementation.

    }

    delete stock;  // sorry, I lied

  }

  mutable muduo::MutexLock mutex_;

  std::map<string, boost::weak_ptr<Stock> > stocks_;

};

}

上一篇 下一篇

猜你喜欢

热点阅读