对象耦合的非线程安全
对象的关系:
组合:一个对象B是对象A的成员,这种关系A对B有生杀大权,B的生命期可控制没什么问题
关联:对象A知道对象B的属性和方法,A将对B操作,这种情形下A怎么知道B还活着?若A操作了不存在的B?还线程安全吗?
聚合:和关联类似,不过是强关联关系,即多了整体和局部的关系,比如:大象A拥有大象牙B...但是A怎么知道自己睡觉的时候B已经被人拔走了?还线程安全嘛?
NO
看发布者订阅者模式:若订阅者想不再关注发布者,发布者怎么知道订阅者已经走了?一个处理方式是订阅者在走之前告诉发布者我要解除与你的关联关系,即在析构函数中通知发布者删除与我的关系,但是这样真的ok了?万一订阅者在析构的过程中被切换出CPU了,然后发布者头脑发热想看看这个订阅者的信息,结果发布者访问了半废品订阅者对象,这就危险了,至于什么危险看具体操作了。回过头来说,订阅者在想解除与发布者关系时,他怎么确定发布者还活着?万一早就挂了呢?下面看这个非线程安全的例子...
#include<iostream>#include<unistd.h>#include<pthread.h>#include<list>#include<assert.h>using namespace std;class Observer;//订阅者,脑残粉class Observable{//发布者,博主 public: void register_(Observer* x){//添加一个脑残粉 vec.push_back(x); } void unregister(Observer* x);//删除脑残粉x void notify();//通知脑残粉们本博主有新动态喔~~~ private: list<Observer*> vec;//存放脑残粉的容器};class Observer{//脑残粉 public: Observer(Observable* a):blog(a){//脑残粉持有博主的指针 x=new int(10); } void get(){ blog->register_(this); } void update(){ cout<<"收到博主的新动态"<<endl; } int get_x(){ return *x; } ~Observer(){ delete x;//立即将x删除,则其它线程在此析构期间将访问错误的x sleep(2);//休眠是为了长期在析构中,其它线程还能访问这个半废品对象 blog->unregister(this);//不想做博主的脑残粉了,哼~~~ } private: Observable* blog;// int* x;//这个主要是为了后面验证非线程安全用};void Observable::notify(){//告诉脑残粉们新动态 for(list<Observer*>::iterator it=vec.begin();it!=vec.end();it++){ (*it)->update(); }}void Observable::unregister(Observer* x){//删除脑残粉 cout<<"这里是不是输出10啊? "<<x->get_x()<<endl;//先看看脑残粉的x值,这里用于脑残粉正在析构中但是将x删除了 vec.remove(x);}Observable* a;Observer* b;void* worker1(void *arg){//博主线程 a->unregister(b);//删除脑残粉}void* worker2(void* arg){//脑残粉线程 b->~Observer();//长期停留在析构函数中但是刚开始就把x删除了,博主会访问到错误的x值}int main(){ pthread_t pid1,pid2; a=new Observable; b=new Observer(a); b->get(); a->notify(); int ret=pthread_create(&pid2,NULL,worker2,NULL); assert(ret==0); ret=pthread_create(&pid1,NULL,worker1,NULL); assert(ret==0); pthread_join(pid1,NULL); pthread_join(pid2,NULL); return 0;}
收到博主的新动态
这里是不是输出10啊? 0 //可见发布者在订阅者析构过程中已经删除的x
这里是不是输出10啊? 0 //这怎么多了个输出?谁能告诉我?太晚了,脑袋模糊了....
解决办法:
说到底是对象生命周期的控制问题,借用BOOST的带有计数功能的智能指针shared_ptr管理对象的声明周期可以完美解决。