2012年6月30日下午将在深圳做《Muduo 网络库:现代非阻塞C++网络编程》演讲,
这是PPT:
http://www.slideshare.net/chenshuo/muduo-network-library
演讲视频:
http://v.youku.com/v_show/id_XNDIyNDc5MDMy.html
http://youtu.be/YDnCAs894Bg
活动介绍:
http://ouropensource.51qiangzuo.com/
posted on 2012-07-01 23:55 陈硕 阅读(5356) 评论(29) 编辑 收藏 引用 所属分类: muduo
看过这个代码,对其中一些设计很不感冒例如 channel明明可以设计成虚函数接口,结果却硬是用std::function去撸陈硕估计老是想着用function了。却忘记了虚函数的使用。例如用boost::bind取代虚函数这篇文章。一个对象级别的函数指针当然可以取代类级别的虚函数,问题是我需要用大炮打蚊子吗?类带一个函数指针与每个对象都带一个函数指针,抛开别的不说,就内存都要节省一些。而且用了boost::function调试起来看到一块块模板栈是不是有想死的感觉?这也算是学会用std::function std::bind的一些人的通病了,啥地方都去用std::function。有玩弄技巧之嫌。说个搞笑的事情,我们原来的头自从学会用bind这个东西后,没有bind就写不出代码了…… 回复 更多评论
@唐诗如果把Channel class设计成虚函数接口,那么在下面这五处用到Channel事件回调的地方,要么各自派生一个 inner DerivedChannel class,要么他们都直接继承Channel,两种做法问题都更大。Acceptor::acceptChannel_Connector::channel_EventLoop::wakeupChannel_TcpConnection::channel_TimerQueue::timerfdChannel_另见:muduo.chenshuo.com/2012/07/modern-c-api-in-muduo-part-1.html 回复 更多评论
总是没有c来的简洁 回复 更多评论
void EventLoop::runInLoop(const Functor& cb)函数中,wakeup可能会被调用2次第一次 在queueInLoop中第二次 自己调用 回复 更多评论
@杨军谢谢,会在下一版修正。 回复 更多评论
@陈硕Channel class设计成虚函数,直接继承Channel有啥问题? 回复 更多评论
@唐诗你的意思是说:Acceptor is-a ChannelConnector is-a ChannelEventLoop is-a ChannelTcpConnection is-a ChannelTimerQueue is-a Channel像这样建模?OO 中毒太深了吧? 回复 更多评论
@陈硕为什么不可以?理由? 回复 更多评论
@唐诗除了我前面给的链接里那篇文章里给的原因,更重要的理由就是:我认为这么建模是错的。Acceptor is-not-a Channel, Acceptor uses a Channel to get readable event notification.Connector is-not-a Channel, Connector uses a Channel to get writable event notification.如此等等。在muduo里,EPollPoller is-a Poller,PollPoller is-a Poller.因此这里用了虚函数。其他地方 is-a 关系不成立。继承不是为了复用,而是为了被复用。 回复 更多评论
@陈硕更重要的理由就是:我认为这么建模是错的。Acceptor is-not-a Channel, Acceptor uses a Channel to get readable event notification.Connector is-not-a Channel, Connector uses a Channel to get writable event notification.这只是文字上的理解的区别,既然可以用虚函数,那必然可以换个理解,例如:我们可以这么理解, 把Channel改个名字EventHandlerAcceptor is a EventHandlerConnector is a EventHandler 回复 更多评论
@唐诗改成 EventHandler 一样是错的,is-a 关系必须满足 Liskov 替换原则:凡是程序里需要用到 EventHandler 的地方,换成它的任何一个派生类都是可行的。但是显然 Acceptor、Connector 等等不具备这种可替换性。 回复 更多评论
TimerQueue::getExpired函数中assert(end == timers_.end() || now < end->first);应该改为now<=end->first吧或者用upper_bound代替lower_bound 回复 更多评论
@杨军不改。你写个程序测一测吧。 回复 更多评论
@陈硕Liskov 替换原则是个过于理想化的原则,实际使用中需要权衡 回复 更多评论
iterator lower_bound ( const key_type& x ) const;返回一个指向大于或者等X值的第一个元素的迭代器。 返回一个指向容器中第一个大于或等于x值的元素的迭代器参数x要比较的值返回值返回一个指向容器中第一个大于或等于x值的元素的迭代器#include <iostream>#include <set>using namespace std;int main (){ set<int> myset; set<int>::iterator it,itlow,itup; for (int i=1; i<10; i++) myset.insert(i*10); // 10 20 30 40 50 60 70 80 90 itlow=myset.lower_bound (30); // ^ itup=myset.upper_bound (60); // ^ myset.erase(itlow,itup); // 10 20 70 80 90 cout << "myset contains:"; for (it=myset.begin(); it!=myset.end(); it++) cout << " " << *it; cout << endl; return 0;} 回复 更多评论
@杨军你认为 muduo 代码中的 UINTPTR_MAX 的作用是什么?https://gist.github.com/3059083 回复 更多评论
喔,受教了,麻烦了啊 回复 更多评论
size_t n = connections_.erase(conn->name()); (void)n;经常看你在返回值前面加一个void,这是为什么啊 回复 更多评论
@杨军你试试去掉它,然后用 BUILD_TYPE=release ./build.sh编译。 回复 更多评论
@陈硕了解,不过(void)n;这种类似的语句也会带来额外的代码在C#里面,经常用2套代码#DEBUG#END#RELEASE#END来解决,不过太繁琐了,不知道有没有更优雅的解决办法 回复 更多评论
@杨军> 不过(void)n;这种类似的语句也会带来额外的代码Are you sure? 回复 更多评论
一定要纠结学术上的Liskov 替换原则可以设计两个一模一样的接口,ConnectionEventHandler和DataEventHandler,两者成员函数完全一样,Acceptor、Connector 分别继承这两个类,这样也比到处是boost::bind要好,core掉的时候就不用看着到处的模板目瞪口呆了不过,既然是一模一样,顺其自然到不如就共用一个接口类了 回复 更多评论
@唐诗ConnectionEventHandler 和 DataEventHandler 二者的成员函数当然不一样,Connector 关心的是 writable event,Acceptor 关心的是 readable event。说到底你说因为debug的原因而影响设计,但是 boost::bind 真的有那么难调试吗?我故意制造一个core dump,调用栈一样容易看嘛。一眼看出 muduo::net::Channel::handleEventWithGuard 调用了 cdns::Resolver::onRead,有困难吗?(gdb) bt#0 cdns::Resolver::onRead (this=0x7ffff7c7ee90, sockfd=6, t=...) at /home/schen/muduo/examples/cdns/Resolver.cc:102#1 0x000000000041011a in boost::function1<void, muduo::Timestamp>::operator() (this=0xc32ae0, receiveTime=<value optimized out>) at /usr/include/boost/function/function_template.hpp:1013#2 muduo::net::Channel::handleEventWithGuard (this=0xc32ae0, receiveTime=<value optimized out>) at /home/schen/muduo/muduo/net/Channel.cc:90#3 0x00000000004102fb in muduo::net::Channel::handleEvent (this=0xc32ae0, receiveTime=<value optimized out>) at /home/schen/muduo/muduo/net/Channel.cc:65#4 0x00000000004131b5 in muduo::net::EventLoop::loop (this=0x7ffff7c7ede0) at /home/schen/muduo/muduo/net/EventLoop.cc:122#5 0x000000000040d5c2 in main (argc=<value optimized out>, argv=0x7ffff7c7f0a8) at /home/schen/muduo/examples/cdns/dns.cc:51 回复 更多评论
server_threaded_efficient和server_threaded_highperformance算法太精妙了,看了回味无穷啊,太牛逼了 回复 更多评论
你好,我最近想搞个网络服务器,参考了你的muduo代码,在阅读过程中,有些地方有点疑问,希望解答,谢谢。 1、epoll_wait检查到有准备好io的描述符后,在获取描述符的时候执行了: 96 channel->set_revents(events_[i].events); 修改了chanel里关注的event为epoll_wait当前检查到的event,并在后面的: void Channel::handleEventWithGuard(Timestamp receiveTime) 直接取 revents_ 与EPOLLIN等进行比较,有个问题,如果原来某个描述同时关注In和out事件,这时客户端消息来了,可读,则因为 set_revents()的调用,以后就不会再关注out事件了,这样会导致有些数据不会写入socket? 2、 72 void TcpConnection::send(const void* data, size_t len)中: 82 string message(static_cast<const char*>(data), len); ===直接用string,如果data中含有二进制数据,如0,不是会丢失吗,而且如果这个段data很长,会发生一次数据拷贝吧? 关于send的问题,我现在希望从业务逻辑线程往某个socket写数据,我想可以直接先试着写入socket的buff,然后失败才写到connection的缓冲区中,并吧socket的epoll状态modify为也关注out,这样是否有问题呢? 回复 更多评论
@xxf 第一个问题应该是我错了,此处只是修改了应用程序中的event,而epoll中还是继续关注原先的事件。 第二个关于缓冲区的问题,如果其他某个线程x要发送数据到某个socket,应该是递交一个请求给io线程y,而不应该直接在x线程中直接send,也就是吧io操作都集中到x线程中,直觉应该是这样,否则x线程就可能因为send失败而进行其他的或者反复的io,是否还有其他理由呢? 在此类操作非常热门频繁的情况下,比如某个线程从另一个进程接受数据,然后要用io线程中的某个socket给客户端转发这个消息,如果用muduo中的send(void*, size_t len)方式,会发生大量的内存new、delete操作,是否可以考虑直接递送给connection的send_buf? 回复 更多评论
@xxf2. 直接用string,如果data中含有二进制数据,如0,不是会丢失吗,而且如果这个段data很长,会发生一次数据拷贝吧?有'\0'也不会丢失,你试试就知道。如果跨线程发送消息,是会有一次拷贝,在C++11里可以用 move semantic 解决。 回复 更多评论
@陈硕非常支持博主的分享精神;我重点关注了博主对tcp发送数据的处理;确实wakeup会调用多次;另外eventfd的一对read/write操作,接近300W次/s(至强E5-2650)感觉还是有点耗;不知是否能从程序角度尽量减少这种为发数据所做的唤醒。该问题我考虑了几天,但结论仅仅是在tcpsocket注册了EPOLLOUT时不需要唤醒,一旦数据发送完全,移除EPOLLOUT事件,每次发数据还是需要唤醒的 回复 更多评论
@bertyoung真要在乎这个,就不要跨线程发送数据。当前IO线程发送数据是不会wakeup的。 回复 更多评论
Powered by: C++博客 Copyright © 陈硕