bind : 传递值. 会导致shared_ptr的引用计数++
muduo中TCPConnectionPtr的生命周期介绍
bind 只传递值 而非 引用
在函数传参时,可能发生shared_ptr的拷贝,造成引用计数++
- pass by value : 如果传入的是个左值,会发生拷贝构造,引用计数++;如果传入的是个右值(即临时构造的shared_ptr),那么发生移动构造,引用计数不变
- pass by reference 如const& : 不拷贝,引用计数不变
- pass by value : 即函数的形参是一个非引用或者指针。pass by reference : 即函数的形参是一个引用&。
那么,bind class的成员函数时,由于要传入一个class对象的指针
- 这是一个pass by value的过程
- 我们绑定上去的指针可以是shared_ptr/裸指针,但不能是weak_ptr
- 如果是以左值传入shared_ptr对象,那么引用计数++
- 如果是以右值传入shared_ptr临时量 / 或者传入裸指针,那么引用计数当然不变
bind –> tuple
- 只能pass by value,而不能pass by reference!!!!
- pass by value : 如果传入的是个左值,会发生拷贝构造 ; 如果传入的是个右值,那么发生移动构造
- 哪怕这个函数的形参就是个 引用&,但是我们如果绑定的话,绑定上去的对象也是会进行一个拷贝 然后赋值给形参!!(与直觉不同,因为我们正常形参是引用的话那么传入的也应当是一个引用,但是这里bind绑定的其实是一个值)(见f6 f7)
testCode.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156shared_from_this() 返回的是一个this的shared_ptr的临时量
class TestClass : public std::enable_shared_from_this<TestClass>
{
public:
using call_back = function<void(void)>;
void hello()
{
cout<<"hello"<<endl;
}
void testFunc()
{
cout<<"========testFunc start========"<<endl;
// shared_from_this() return a 临时的 shared_ptr,引用计数++。如果不用变量保存下来的话,这条语句过了之后就--。也就是只有这一条语句的use_count变了+1
cout<<"+ shared_from_this() " << shared_from_this().use_count() <<endl;
cout<<"========testFunc end========"<<endl;
}
void testBind()
{
cout<<"=======testBind start=========="<<endl;
cb_ = std::bind(&TestClass::hello,shared_from_this()); // ++
cb_();
cout<<"bind + shared_from_this() = "<<shared_from_this().use_count()<<endl; // ++ --
cout<<"=======testBind end=========="<<endl;
}
void testPassAsValue(shared_ptr<TestClass> p) // ++
{
cout<<"============testPassAsValue start============"<<endl;
cout<<p.use_count()<<endl;
cout<<"============testPassAsValue end============"<<endl;
}
void testPassAsRef(const shared_ptr<TestClass>& p)
{
cout<<"==========testPassAsRef start=============="<<endl;
cout<<p.use_count()<<endl;
cout<<"==========testPassAsRef end================"<<endl;
}
~TestClass(){
cout<<"~TestClass()"<<endl;
}
private:
call_back cb_;
};
int main()
{
std::shared_ptr<TestClass> sp(new TestClass());
cout<<sp.use_count()<<endl; // 1
std::weak_ptr<TestClass> wp(sp);
cout<<sp.use_count()<<endl; // 1
cout<<"********************************"<<endl;
// bind 绑定指针 是传值 ,会导致引用计数 ++
std::bind(&TestClass::testFunc,sp)(); // 3 = 1(sp) + 1(bind) + 1 (shared_from_this())
cout<<sp.use_count()<<endl; // 1
cout<<"********************************"<<endl;
function<void(void)> f1 = std::bind(&TestClass::testBind,sp);
cout<<sp.use_count()<<endl; // 2 = 1(sp) + 1(f1 bind)
f1(); // 4 = 1(sp) + 1(bind) + 1(bind) + 1(shared_from_this())
cout<<sp.use_count()<<endl; // 3 = 1(sp) + 1(f1 bind) + 1(cb_ bind)
cout<<"********************************"<<endl;
function<void(shared_ptr<TestClass>)> f2 = std::bind(&TestClass::testPassAsValue,sp,std::placeholders::_1); // 4 = 1(sp) + 1(f1 bind) + 1(cb_ bind) + 1(f2 bind)
cout<<sp.use_count()<<endl; // 4
f1(); // 5
f2(sp); // 5
cout<<"********************************"<<endl;
function<void(const shared_ptr<TestClass>& p)> f3 = std::bind(&TestClass::testPassAsRef,sp,std::placeholders::_1);
cout<<sp.use_count()<<endl; // 5 = 4 + 1(f3 bind)
f3(sp); // 5 = 4 + 1(f3 bind) 传引用不是传值 不会导致引用计数++
cout<<"********************************"<<endl;
function<void(void)> f4 = std::bind(&TestClass::testPassAsValue,sp,sp);
cout<<sp.use_count()<<endl; // 7 = 5 + 1(f4 bind) +1(f4 bind)
// pass by value but 传入右值
cout<<"********************************"<<endl;
function<void(void)> f5 = std::bind(&TestClass::testFunc, shared_ptr<TestClass>(new TestClass())); // 移动构造
f5(); // 2 = bind (临时量移动构造给别人) + shared_from_this()
cout<<"*******************************************"<<endl;
// 下面证明了bind只能传递值
**哪怕这个函数的形参就是个 引用&,但是我们如果绑定的话,绑定上去的对象也是会进行一个拷贝 然后赋值给形参!!**
shared_ptr<TestClass> p(new TestClass());
function<void(void)> f6 = std::bind(&TestClass::testPassAsRef,p,p);
cout<<p.use_count()<<endl; // 3 = 1(define) + 1(bind) + 1(bind bind只能传值 )
f6(); // 3
cout<<"*******************************************"<<endl;
shared_ptr<TestClass> p2(new TestClass());
function<void(const shared_ptr<TestClass> & )> f7 = std::bind(&TestClass::testPassAsRef,p2,std::placeholders::_1);
cout<<p2.use_count()<<endl; // 2 = 1(define) + 1(bind)
f7(p2); // 2 = 1(define) + 1(bind) 与上面的区别在于 bind 只能传值 而 这个形参 我们在调用的时候 是可以直接填引用进去的
}
1
1
********************************
========testFunc start========
+ shared_from_this() 3
========testFunc end========
1
********************************
2
=======testBind start==========
hello
bind + shared_from_this() = 4
=======testBind end==========
3
********************************
4
=======testBind start==========
hello
bind + shared_from_this() = 5
=======testBind end==========
============testPassAsValue start============
5
============testPassAsValue end============
********************************
5
==========testPassAsRef start==============
5
==========testPassAsRef end================
********************************
7
********************************
========testFunc start========
+ shared_from_this() 2
========testFunc end========
*******************************************
3
==========testPassAsRef start==============
3
==========testPassAsRef end================
*******************************************
2
==========testPassAsRef start==============
2
==========testPassAsRef end================
~TestClass()
~TestClass()
~TestClass()
Process finished with exit code 0
- 还有,易知,weak_ptr的复制不会导致shared_ptr的引用计数改变
TcpConnectionPtr的生命周期
TcpConnection的引用存在于
- TcpServer中的
connections_<name,TcpConnectionPtr>
。shared_ptr。引用计数 = 1 - channel 中的weak_ptr 当handleEvent时 会lock为shared_ptr(即TcpConnectionPtr) 。引用计数++ 保证不在handleEvent时析构TcpConnection
- 还有就是在连接断开时的局部变量TcpConnectionPtr 以及 bind参数时候的拷贝TcpConnectionPtr。
- TcpServer中的
一个TcpConnection何时析构?
- 断开连接后,TcpConnectionPtr = 0
- 即整个handleEvent(handleClose)结束之后(最后一个成员函数是connetionDestroyed),TcpConnectionPtr = 0,此时TcpConnection析构(channel也就跟着析构)
将一条连接断开都需要做什么操作
- 大概来讲 就是
- 取消对该fd的监听 :channel_->disableAll() — TcpConnection内做(因为这是TcpConnection封装的成员)
- 执行user设置的callback:connectionCallback_(connPtr); — TcpConnection内做
- 将TcpConnection从TcpServer中的记录移除:connections_.erase(conn->name()); — TcpServer内做(因为这是TcpServer封装的成员)
- channel从poller中删除掉 — TcpConnection内做
- 大概来讲 就是
TcpConnection析构流程 / 断开连接流程
poller -> channel::closeCallback -> TcpConnection::handleClose -> TcpServer::removeConnection -> TcpConnection::connectionDestoyed
在进入handleClose之后,不必担心TcpConnection会在执行完成前就被销毁,因为进入之前 就已经用一个weak_ptr做了lock提升,保证了引用计数至少为1
去掉一块错误注释 其余不变
相关代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51// poller -> channel::closeCallback -> TcpConnection::handleClose -> TcpServer::removeConnection -> TcpConnection::connectionDestoyed
// 之前注册给channel的关于关闭连接的回调操作都已经完成
// 且TcpConnection也一直保持着没有析构。
// 该函数结束之后,TcpConnection就会几乎马上被析构!
void TcpConnection::handleClose()
{
TcpConnectionPtr connPtr(shared_from_this());
LOG_INFO("shared_ptr cnt %ld",connPtr.use_count());
// 3 = 1(channel::tie_.lock) + 1(TcpServer::connections_[connName] = conn;) + 1(connPtr)
// TcpServer::removeConnection
// **connections_.erase(TcpConnectionPtr)**
closeCallback_(connPtr);
LOG_INFO("shared_ptr cnt %ld tcpconnection %p point to %p",connPtr.use_count(),connPtr.get(),this);
// 2 = 1(channel::tie_.lock) + 1(connPtr)
}
--------------------------------------------------------------
// removeConnection -> removeConnectionInLoop
void TcpServer::removeConnection(const TcpConnectionPtr &conn)
{
// 这里也证明了 即便形参是一个引用 即便正常调用函数传递的是个引用,可是,只要bind了,传递的就是一个值 会拷贝
LOG_INFO("shared_ptr cnt %ld\n",conn.use_count()); // 3 没变
loop_->runInLoop(
std::bind(&TcpServer::removeConnectionInLoop, this, conn));
LOG_INFO("shared_ptr cnt %ld\n",conn.use_count()); // 2
}
// handleRead n = 0 -> handleClose - >removeConnection -> removeConnectionInLoop -> TcpConnection::connectionDestroyed
void TcpServer::removeConnectionInLoop(const TcpConnectionPtr &conn)
{
LOG_INFO("shared_ptr cnt %ld",conn.use_count()); // 4 = 3+1 因为bind是拷贝值 会导致引用计数++
// tcpconnection use count -- : tcpserver 从记录中删除该连接
connections_.erase(conn->name());
LOG_INFO("after erase shared_ptr cnt %ld",conn.use_count()); // 3 = 1(channel::tie_.lock) + 1(connPtr) + 1(bind拷贝conn)
(conn->getLoop())->runInLoop(
std::bind(&TcpConnection::connectionDestroyed,conn.get())); // 根据以上分析 传递裸指针也可 反正在这个函数里TcpConnectionPtr的引用计数一定不是0
// connectionDestroyed : TcpConnection析构前调用的最后一个成员函数
}
// 连接销毁
void TcpConnection::connectionDestroyed()
{
channel_->remove(); // 把channel从poller中删除掉
}连接断开时的相关日志如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31[INFO] 2022-10-27-22-07-43.011788 : /home/shc/Muduo/src/TcpConnection.cpp (135) <handleClose> TcpConnection::handleClose fd = 6 state = 3
[INFO] 2022-10-27-22-07-43.011795 : /home/shc/Muduo/src/EpollPoller.cpp (37) <updateChannel> fd = 6 , events = 0 , index = 1
[INFO] 2022-10-27-22-07-43.011807 : /home/shc/Muduo/src/EpollPoller.cpp (90) <update> fd = 6 is to be epolled or deleted in loop 0x7ffcce544ab0 on thread 8121
[INFO] 2022-10-27-22-07-43.011833 : /home/shc/Muduo/src/TcpConnection.cpp (146) <handleClose> shared_ptr cnt 3
[INFO] 2022-10-27-22-07-43.011856 : /home/shc/Muduo/src/TcpConnection.cpp (148) <handleClose> shared_ptr cnt 3
[INFO] 2022-10-27-22-07-43.011883 : testserver.cpp (40) <onConnection> connection down : from 127.0.0.1:50216 to 127.0.0.1:6667!
[INFO] 2022-10-27-22-07-43.011944 : /home/shc/Muduo/src/TcpConnection.cpp (153) <handleClose> shared_ptr cnt 3
[INFO] 2022-10-27-22-07-43.011981 : /home/shc/Muduo/src/TcpServer.cpp (120) <removeConnection> shared_ptr cnt 3
[INFO] 2022-10-27-22-07-43.012043 : /home/shc/Muduo/src/EventLoop.cpp (149) <runInLoop> cb runInLoop in loop 0x7ffcce544ab0 on thread 8121
[INFO] 2022-10-27-22-07-43.012071 : /home/shc/Muduo/src/EventLoop.cpp (155) <runInLoop> 直接调用cb on loop 0x7ffcce544ab0 in thread 8121
[INFO] 2022-10-27-22-07-43.012101 : /home/shc/Muduo/src/TcpServer.cpp (130) <removeConnectionInLoop> shared_ptr cnt 4
[INFO] 2022-10-27-22-07-43.012126 : /home/shc/Muduo/src/TcpServer.cpp (131) <removeConnectionInLoop> TcpServer::removeConnectionInLoop [EchoServer] - connection EchoServer-127.0.0.1:6667#1
[INFO] 2022-10-27-22-07-43.012169 : /home/shc/Muduo/src/TcpServer.cpp (137) <removeConnectionInLoop> after erase shared_ptr cnt 3
[INFO] 2022-10-27-22-07-43.012195 : /home/shc/Muduo/src/EventLoop.cpp (149) <runInLoop> cb runInLoop in loop 0x7ffcce544ab0 on thread 8121
[INFO] 2022-10-27-22-07-43.012220 : /home/shc/Muduo/src/EventLoop.cpp (155) <runInLoop> 直接调用cb on loop 0x7ffcce544ab0 in thread 8121
[INFO] 2022-10-27-22-07-43.012228 : /home/shc/Muduo/src/TcpConnection.cpp (332) <connectionDestroyed> state_ = 0
[INFO] 2022-10-27-22-07-43.012252 : /home/shc/Muduo/src/EpollPoller.cpp (109) <removeChannel> fd = 6 , events = 0 , index = 2 in loop 0x7ffcce544ab0 on thread 8121
[INFO] 2022-10-27-22-07-43.012274 : /home/shc/Muduo/src/EpollPoller.cpp (90) <update> fd = 6 is to be epolled or deleted in loop 0x7ffcce544ab0 on thread 8121
[INFO] 2022-10-27-22-07-43.012311 : /home/shc/Muduo/src/TcpServer.cpp (148) <removeConnectionInLoop> after removeConnectionInLoop
[INFO] 2022-10-27-22-07-43.012344 : /home/shc/Muduo/src/TcpServer.cpp (124) <removeConnection> shared_ptr cnt 2
[INFO] 2022-10-27-22-07-43.012376 : /home/shc/Muduo/src/TcpConnection.cpp (160) <handleClose> shared_ptr cnt 2 tcpconnection 0x56195873ad90 point to 0x56195873ad90
[INFO] 2022-10-27-22-07-43.012405 : /home/shc/Muduo/src/TcpConnection.cpp (45) <~TcpConnection> TcpConnection::ctor (0x56195873ad90) [EchoServer-127.0.0.1:6667#1] on socket 6 with state 0