《Linux多线程服务端编程》学习笔记---第一章

多线程下管理对象会遇到的问题:

(1)对象析构时可能别的线程在执行该对象的成员函数

(2)成员函数执行时该对象正在被别的线程析构

(3)在调用某对象时如何得知该对象还未被析构

对这些问题进一步看就是要保证多线程环境下对象创建与析构的安全性。

对于创建,关键是不要在创建过程中泄露this指针,如果泄露了调用者会在对象还未构造完成之前就开始使用这一对象,如果对象是基类对象,其派生类还未构造,则问题更为严重,所以要采用构造函数+initialize()的二段式构造。


对于析构,本来只要用锁控制多个线程顺序执行就能保证临界区的安全性,但是由于析构过程会销毁锁,所以各成员函数对临界区的访问不再受锁保护,比如,线程A获得了锁进入临界区,线程B未获得只要阻塞,但线程A在临界区了把对象析构了,那么线程B可能永远阻塞下去,或者即便进入它使用的也是一个不复存在的对象。


为了进一步说明多线程环境下对象析构存在的问题,可以结合设计模式的例子来论述,组合由于把对象生命期管理交由其唯一拥有者管理,故不存在问题,但关联与聚合,由于一个对象只是持有了另一对象的指针,所以其中一个对象就要使用一个生命期不由他管理的对象。可以举observer模式来谈,观察者持有被观察对象的指针,被观察者现在要通知观察者,它可以在通知前先看看观察对象是否还活着,但就在检查出观察者对象还活着要调用它的成员函数时,观察者对象开始析构,那么被观察对象调用的就是一个正在析构对象的成员函数,由于析构过程会清除虚函数指针,所以若是虚函数后果更加不堪设想。


为了解决上述问题无法有效的控制对象的生命期是造成多线程环境不安全这一问题,就要用shared_ptr来解决,它该如何解决上述observer模式提到的问题呢?可以在被观察对象中持有观察者对象的weak_ptr,然后在通知观察者的成员函数中再用shared_ptr尝试提升,若提升成功则此时引用计数大于等于2,可调用成员函数,若提升失败则说明别的线程已经把它销毁了,这时就要从weak_ptr中把这个对象删除掉,之所以能这么做因为weak_ptr并不控制对象生命期但知道对象是否还活着,并且观察者对象也会持有被观察者的shared_ptr,如果不用weak_ptr可能带来循环引用的问题,循环引用该如何解决呢?可以看这里:

http://blog.csdn.net/segen_jaa/article/details/8080167

http://blog.csdn.net/jfkidear/article/details/9034455

总之,weak_ptr在解决这一问题时与shared_ptr配合工作起到了对象池,弱回调(帮助shared_ptr检测对象,若存在则调用其成员函数,否则忽略)的作用。


再总结下shared_ptr,它的计数时原子操作,因此是安全的,读写shared_ptr对象是不能原子化的,它所达到的效果是一旦对象不再被引用,系统刻不容缓回收内存。

它能解决的问题有(这些问题对于原始指针很常见):

(1)避免空悬指针与野指针

(2)防止缓冲区溢出

(3)避免内存重复释放

(4)内存泄露

(5)不配对的new/delete

(6)提供了线程的RAII,比如把锁资源放到shared_ptr中作为栈对象管理时,可以保证函数抛掷异常时对象的正确释放。

shared_ptr所带来的问题有:

(1)意外延长对象的生命期,特别是shared_ptr允许拷贝与赋值,如果不小心遗留了拷贝,则对象永远不会销毁

(2)比原始指针更多的拷贝开销。

(3)在某一线程析构对象的成本,因为一旦不再有shared_ptr指向对象,该对象就要析构,即便这一对象不在此对象中诞生,但也必须在这一线程中销毁,而销毁可能比较耗时,从而拖慢了线程。

(4)循环引用。

另外,还可以在创建shared_ptr定义其析构动作,这里理解的不太好,还需深究。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值