文章目录
1.TcpConnection生存期管理
-
连接关闭时序图
(1)一个TcpServer维护了一个已连接列表,当一个连接关闭的时候,TcpConnection的通道处于活跃状态,EventLoop的事件循环返回这个通道,并且调用handleEvent来处理;
连接关闭属于可读事件,这里回调了TcpConnection的handleRead(),handleRead()调用read()返回为0,判断返回为0,则又调用了handleClose(),handleClose()中又回调了TcpServer中的removeConnection(),erase将连接对象从已连接列表中移除。
(2)这里不要立即销毁连接对象TcpConnection,若销毁了TcpConnection对象,则TcpConnection对象所包含的Channel对象也会跟着销毁,而我们当前正在调用Channel对象的handleEvent(),而这个Channel对象销毁了,就会出现core dump。也即是说,TcpConnection对象的生存期要长于handleEvent()。
(3)可以使用shared_ptr来管理TcpConnection对象。
(i)当连接到来,创建一个TcpConnection对象,立即用shared_ptr来管理,引用计数为1,
当Channel中维护一个weak_ptr(tie_),弱指针,将这个shared_ptr对象赋值给tie_,引用计数仍为1(由于weak_ptr是弱引用,其引用计数不会+1);
(ii)当连接关闭,在handleEvent,将tie_提升,得到一个shared_ptr对象,引用计数就变成了2,此时erase从列表中移除,引用计数2->1,此时TcpConnection对象不会销毁。
此外muduo的做法是:调用queueInLoop()将connectDestroyed()放到EventLoop的functors中(利用boost::bind得到一个boost::function放到functors列表中)。接着又将TcpConnection对象的引用计数+1,此时=2,handleEvent()返回后,所提升的shared_ptr局部对象销毁了,引用计数又-1,此时=1,接着boost::function又调用了用户的回调函数connCb(),调用完毕后,引用计数1->0,此时TcpConnection对象才销毁了。
(iii)连接断开和连接建立都是调用的connCb(),removeConnection()连接断开回调函数是内部的
-
eg:34\jmuduo\muduo\net\TcpConnection.h
34\jmuduo\muduo\net\TcpConnection.cc
34\jmuduo\muduo\net\Channel.h
34\jmuduo\muduo\net\Channel.cc
34\jmuduo\muduo\net\TcpServer.h
34\jmuduo\muduo\net\TcpServer.cc -
eg测试:34\jmuduo\tests\Reactor_test09.cc
34\jmuduo\tests\CMakeLists.txt -
测试:仅看看引用计数的情况
客户端使用telnet模拟连接建立于断开
这里处理了连接断开事件,服务端是不会出现busy-loop的
-
继承public boost::enable_shared_from_this的原因
eg:34\esft.cpp
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <cassert>
class Y: public boost::enable_shared_from_this<Y>
{
public:
boost::shared_ptr<Y> f()
{
return shared_from_this();
}
Y* f2()
{
return this;
}
};
int main()
{
boost::shared_ptr<Y> p(new Y);//此时p的引用计数=1
boost::shared_ptr<Y> q = p->f();//p->f()表示当前对象转换称为shared_ptr,此时引用计数=2
Y* r = p->f2();
assert(p == q);//断言其内部的引用计数是否相等
assert(p.get() == r);//p.get()表示p里面的指针,其实就是new Y这个指针
std::cout<<p.use_count()<<std::endl;
// 这里构造了一个新的shared_ptr对象,并不是将一个shared_ptr对象赋值给另一个shared_ptr对象,后者的话引用计数会+1
boost::shared_ptr<Y> s(r);//这里的r就是new Y这个指针,虽然2个指针的值是一样的,但是不代表shared_ptr引用计数+1,因为这里是构造了一个新的shared_ptr对象
//这里的r是裸指针,构造了一个新的shared_ptr对象
//这里若想要引用计数值+1,必须是p对象拷贝构造s,或者将对象p赋值给s
std::cout<<s.use_count()<<std::endl;
assert(p == s);//这里断言会失败
return 0;
}
测试:
为什么要用 enable_shared_from_this?
- 需要在类对象的内部中获得一个指向当前对象的 shared_ptr 对象。
- 如果在一个程序中,对象内存的生命周期全部由智能指针来管理。在这种情况下,要在一个类的成员函数中,对外部返回 this 指针就成了一个很棘手的问题。
什么时候用?
- 当一个类被共享智能指针 share_ptr 管理,且在类的成员函数里需要把当前类对象作为参数传给其他函数时,这时就需要传递一个指向自身的 share_ptr。
- 如何安全地将 this 指针返回给调用者?
#include <iostream>
#include <stdlib.h>
#include <memory>
using namespace std;
// 比较推荐的写法
struct Good : std::enable_shared_from_this<Good> // note: public inheritance
{
std::shared_ptr<Good> getptr() {
return shared_from_this();
}
};
// 错误的用法:用不安全的表达式试图获得 this 的 shared_ptr 对象
struct Bad
{
std::shared_ptr<Bad> getptr() {
return std::shared_ptr<Bad>(this);
}
~Bad() { std::cout << "Bad::~Bad() called\n"; }
};
int main()
{
// 正确的用法: 两个 shared_ptr 共享同一个对象
std::shared_ptr<Good> gp1 = std::make_shared<Good>();
std::shared_ptr<Good> gp2 = gp1->getptr();
std::cout << "gp2.use_count() = " << gp2.use_count() << '\n';
// 错误的用法: 调用 shared_from_this 但其没有被 std::shared_ptr 占有
try {
Good not_so_good;
std::shared_ptr<Good> gp1 = not_so_good.getptr();
}
catch(std::bad_weak_ptr& e) {
// 在 C++17 之前,编译器不能捕获 enable_shared_from_this 抛出的std::bad_weak_ptr 异常
// 这是在C++17之后才有的特性
std::cout << e.what() << '\n';
}
// 错误的用法,每个 shared_ptr 都认为自己是对象的唯一拥有者
// 调用错误的用法,会导致两次析构 Bad的对象,第二次析构时,指针指向的空间已经被析构,
//这样做不会增加引用计数
/*
无法通过原指针增加共享指针的统一引用计数(两个非共享的shared_ptr指向同一个对象,未增加引用计数导对象被析构两次)。调用构造函数方式。
int * pt = new int();
shared_ptr<int> spt1(pt); //调用构造函数
shared_ptr<int> spt2(pt); //调用构造函数
std::cout << "spt1.use_count() = " << spt1.use_count() << std::endl;
std::cout << "spt2.use_count() = " << spt2.use_count() << std::endl;
*/
std::shared_ptr<Bad> bp1 = std::make_shared<Bad>();
//std::shared_ptr<Bad> bp2 = bp1;OK
std::shared_ptr<Bad> bp2 = bp1->getptr();
std::cout << "bp2.use_count() = " << bp2.use_count() << '\n';
return 0;
}
-
测试:当需要返回指向当前对象的 shared_ptr 时,优先使用 enable_shared_from_this 机制。
-
ref:C++11中enable_shared_from_this的用法解析,enable_shared_from_this 机制
-
eg测试:34\jmuduo\tests\Reactor_test09.cc
34\jmuduo\tests\CMakeLists.txt -
测试:客户端发送aaaa6个字节(telnet尾巴还有\r\n),接着关闭客户端
客户端接收了6个字节,且回调了用户的onConnection()函数