(P34)muduo_base库源码分析:TcpConnection生存期管理

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()函数
    在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜欢打篮球的普通人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值