读者来信用黑色,我的回答用蓝色。经过整理,接近对话体。
> 陈硕,你好,
>
> 阅读了你的书,很有收获。
> 但是没有在moduo的源代码里面找到实现线程模型11的例子。即one thread per loop + thread pool。
> 谢谢。
书第 173 页图 6-14 下面的第一段话,具体改动方法参考前一页的 diff。
> 谢谢。
>
> 另外TcpConnection和Channel的生命周期管理有点问题。
> TcpConnection如果已经被回收了,其包含的Channel也已经被回收了。而这个时候在Channel::handleEvent()里面检查tied_和tie_是危险的。因为其内存已经被回收了。
>
> 如果用户保证TcpConnection被回收之后,不会再用Channel的裸指针,则没有必要在TcpConnection::connectEstablished()中call tie().
TcpConnection 回收之前,会调用 connectDestroyed,其中调用 channel_->remove();,这样就不可能再会有 Channel::handleEvent() 被调用了。
tie() 的作用是防止 Channel::handleEvent() 运行期间其 owner 对象析构,导致 Channel 本身被销毁。
> > TcpConnection 回收之前,会调用 connectDestroyed,其中调用 channel_->remove();,这样就不可能再会有 Channel::handleEvent() 被调用了。
> 这个时候会不会有race condition?假设现在有两个active channels,处理头一个的时候回收TcpConnection,而第二个channel刚好对应这个connection。
这时你没有办法强制销毁 TcpConnection,只能降低其引用计数,所以不会有问题。你可以写段代码试试。
> 另外底层的poller OS api是否保证unregister channel之后一定不会再有这个channel的事件,会清空内核的已经就绪的事件队列?
跟内核没关系,Poller class 在 unregister channel 之后就不可能调用其 handleEvent() 成员函数。
> 那EPollPoller::fillActiveChannels()的改一改,“assert(it != channels_.end());”不再适用了,而且每次都个event都要查一次map。效率会有问题。
assert() 只有在 debug build 才执行,不会影响效率。
再说每个 event 都要涉及 read/write 等系统调用,开销比“查一次 map”大得多,优化这里是无用功。
> 但这个assert()不是invalid了吗?你可能之前在unregister channel的时候已经从map里面remove掉了它。
这个 assert 是有效的,你再想想。
> > tie() 的作用是防止 Channel::handleEvent() 运行期间其 owner 对象析构,导致 Channel 本身被销毁。
> 这个也不太make sense。仍然有race conditon。在Channel::handleEvent()拥有guard锁定ownner之前,Channel::handleEvent()需要检查其tied_。
你再想想,tie 的作用是防止调用 handleEvent() 期间对象销毁(比如调用 closeCallback 期间),不是也不可能防止调用 handleEvent() 之前对象销毁。
> 恩,是的。整个TcpConnection, Channel, EventLoop都是一个thread里面run的。