线程安全的对象生命周期2019-10-28
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++标准对构造函数和析构函数中调用虚函数的行为有明确规定,但是没有考虑并发调用的情况。
![](https://img.haomeiwen.com/i7898366/a78e56138bf6bfa0.jpg)
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。
![](https://img.haomeiwen.com/i7898366/f48223a6a6ce43a8.jpg)
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_;
};
}