网络编程实战
盛延敏
前大众点评云平台首席架构师
44207 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 40 讲
网络编程实战
15
15
1.0x
00:00/00:00
登录|注册

27 | I/O多路复用遇上线程:使用poll单线程处理所有I/O事件

I/O操作抽象成事件
事件分发线程
Web编程
GUI编程
pthread线程
fork进程
解决业务逻辑和I/O逻辑混在一起的方法
修改onMessage方法
不同模型的优缺点比较
单线程reactor处理多个连接
回调函数
TCPServer
acceptor
event_loop
single reactor thread + worker threads
single reactor thread
pthread
fork
read、decode、compute、encode、send
反应堆模型
事件驱动模型
性能问题
思考题
总结
样例程序结果
样例程序
几种I/O模型和线程模型设计
I/O多路复用遇上线程
网络编程实战第27讲

该思维导图由 AI 生成,仅供参考

你好,我是盛延敏,这里是网络编程实战第 27 讲,欢迎回来。
我在前面两讲里,分别使用了 fork 进程和 pthread 线程来处理多并发,这两种技术使用简单,但是性能却会随着并发数的上涨而快速下降,并不能满足极端高并发的需求。就像第 24 讲中讲到的一样,这个时候我们需要寻找更好的解决之道,这个解决之道基本的思想就是 I/O 事件分发。
关于代码,你可以去GitHub上查看或下载完整代码。

重温事件驱动

基于事件的程序设计: GUI、Web

事件驱动的好处是占用资源少,效率高,可扩展性强,是支持高性能高并发的不二之选。
如果你熟悉 GUI 编程的话,你就会知道,GUI 设定了一系列的控件,如 Button、Label、文本框等,当我们设计基于控件的程序时,一般都会给 Button 的点击安排一个函数,类似这样:
//按钮点击的事件处理
void onButtonClick(){
}
这个设计的思想是,一个无限循环的事件分发线程在后台运行,一旦用户在界面上产生了某种操作,例如点击了某个 Button,或者点击了某个文本框,一个事件会被产生并放置到事件队列中,这个事件会有一个类似前面的 onButtonClick 回调函数。事件分发线程的任务,就是为每个发生的事件找到对应的事件回调函数并执行它。这样,一个基于事件驱动的 GUI 程序就可以完美地工作了。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文介绍了如何使用I/O多路复用技术结合单线程处理所有I/O事件。作者首先回顾了基于事件驱动的程序设计思想,比如GUI和Web编程中的事件处理机制。然后对比了使用fork、pthread和单reactor线程等不同的I/O模型和线程模型设计,指出它们在处理多并发网络编程中的局限性。接着,作者详细介绍了使用poll单线程处理所有I/O事件的设计思路,并给出了相应的样例程序。该样例程序展示了如何使用event_loop、acceptor和tcp_server等组件来实现单线程处理I/O事件的网络编程框架,并提供了相应的回调函数来处理连接建立、数据读取、数据发送和连接关闭等操作。通过这种设计,可以有效地提高程序的性能和并发处理能力,满足高性能、高并发的需求。整体而言,本文通过实例和技术原理的介绍,为读者提供了一种高效的网络编程解决方案,有助于读者快速了解并应用于实际开发中。文章还提出了两道思考题,鼓励读者深入思考并分享交流。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《网络编程实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(43)

  • 最新
  • 精选
  • 林林
    worker thread 和 reactor thread之间怎么进行数据传递?是要利用队列+锁吗?

    作者回复: 这就是两个普通的producer-consumer线程关系,使用队列+锁的方式是一个比较常见的实现方式。

    2019-11-26
    15
  • 1:事件驱动模型的设计思想是啥? 事件驱动模型的设计的思想是,一个无限循环的事件分发线程在后台运行,一旦做了某种操作触发了一个事件,这个事件就会被放置到事件队列中,事件分发线程的任务,就为这个发生的事件找到对应的事件回调函数并执行它。 这里有个疑问,事件分发线程怎么找到事件的回调函数,并调用它的? 2:事件驱动模型的优势是啥? 事件驱动的好处是占用资源少,效率高,可扩展性强,是支持高性能高并发的不二之选。 老师好,请问占用资源少这个结论是怎么得出来的? 3:IO网络通信是怎么实现事件驱动模型的? 通过使用 poll、epoll 等 I/O 分发技术,可以设计出基于套接字的事件驱动程序,从而满足高性能、高并发的需求。 4:Reactor模型是啥玩意? Reactor模型(中文叫做反应堆模型)也就是事件驱动模型或者是 Event loop 模型。 这个模型的核心有两点。 第一,它存在一个无限循环的事件分发线程,或者叫做 reactor 线程、Event loop 线程。这个事件分发线程的背后,就是 poll、epoll 等 I/O 分发技术的使用。 第二,所有的 I/O 操作都可以抽象成事件,每个事件必须有回调函数来处理。acceptor 上有连接建立成功、已连接套接字上发送缓冲区空出可以写、通信管道 pipe 上有数据可以读,这些都是一个个事件,通过事件分发,这些事件都可以一一被检测,并调用对应的回调函数加以处理。 5:Reactor模型——解决了空闲连接占用资源的问题,Reactor线程只负责处理 I/O 相关的工作,业务逻辑相关的工作都被裁剪成一个一个的小任务,放到线程池里由空闲的线程来执行。当结果完成后,再交给反应堆线程,由Reactor线程通过套接字将结果发送出去。 所以,这个模式性能更优。 6:阻塞IO+多进程——实现简单,性能一般 7:阻塞IO+多线程——相比于阻塞IO+多进程,减少了上下文切换所带来的开销,性能有所提高。 8:阻塞IO+线程池——相比于阻塞IO+多线程,减少了线程频繁创建和销毁的开销,性能有了进一步的提高。 9:Reactor+线程池——相比于阻塞IO+线程池,采用了更加先进的事件驱动设计思想,资源占用少、效率高、扩展性强,是支持高性能高并发场景的利器。

    作者回复: 第一个问题,回调函数和套接字对应的,通过套接字找到对应的回调函数; 第二个问题,因为是事件驱动,不需要分配固定的资源,仅仅使用几个线程就可以支持上万的连接,每个线程的利用率得到了最大提升。

    2019-11-24
    4
    12
  • fxzhang
    老师可否讲解linux下如何开发的,最近想换工作,但是之前都在windows下面开发,想自学一下linux下是如何开发的,但是有一种找不到开头不知道该怎么学习的感觉,很无力

    作者回复: 先学习一下Linux下的安装、配置、管理,把工作环境放到Linux下面,让Linux成为你的工作效率机器; 其次,慢慢学习Bash,感受一下Linux的能力; 接下来就是学习一些 Linux下的程序设计,如I/O、网络等。 如果你能把这篇系列的所有代码都改一遍,运行一遍,就是一个良好的开头。 加油~

    2019-10-09
    10
  • 刘忽悠
    没太理解epoll反应堆模型,和直接eopll的区别是什么? 不知道我这么理解对不对,一般使用epoll,假如新连接建立,注册cfd读事件,当事件触发,接着在主线程里面读出来,然后处理,接着发送;epoll反应堆模式是不是仅仅只是在注册事件的时候加了一个对应的回调函数,当事件触发,然后调用回调去处理?相当于统一了一下接口? 对于老师说的reactor+threadpool不知道理解的对不对,我个人理解是,当有新连接建立,因为监听描述符注册的是acceptor事件,这时候这个事件触发,触发之后注册新的描述符cfd的read事件,当cfd的读事件触发,这时候在reactor线程(主线程)里面调当初注册的回调函数来处理读事件,读出来之后,然后注销cfd的读事件,这时候把读出来的内容封装成Task,放到线程池的Task队列,通知线程池的工作线程——有任务了,唤醒一个线程对任务进行处理,处理完成之后,这时候注册cfd的写事件,然后work线程处理完成,一般情况下写缓冲区都是可以写,所以这时候在reactor线程里面,cfd的写事件被触发,这时候在reactor线程里面调用对应的回调函数把数据发送过去,接着然后注销写事件,注册读事件,继续监听客户端请求。这样一来,业务逻辑都在线程池里面去做,然后读,写都是通过在主线程,也就是reactor线程里面调用对应的回调函数来完成。 不知道这么理解reactor模式+线程池对不对?

    作者回复: 基本是正确的。有一个小小的地方和我的理解不一致,就是对socket的读写,也是可以放到线程池里独立的线程去做,而主reactor线程就是一个事件分发器,不负责I/O操作,因为主reactor线程是一个非常重要的"大脑",尽量不要让它成为瓶颈。

    2020-07-01
    3
    5
  • heyman
    reactor 线程无限循环,有点像轮询,效率不会很低吗?

    作者回复: 不会。这里不是轮询哦,轮询是消耗cpu时间的,这里是系统提供的事件驱动,看似在无限循环,其实这个时候cpu被调度干其他事了,当真正有事件发生,cpu又会被切换回来,所以效率很高。

    2020-04-18
    5
  • 传说中的成大大
    第一遍看完这篇文章 我就感受颇深 尤其是事件触发 这个模式 然后就想到工作当中的用到的skynet框架底层就是采用事件驱动,某个服务有数据达到 就去触发对应的服务,然后再回想工作当中很多逻辑都抽象成事件,通过一个主循环检测时间然后来触发对应的事件! 更重要的一点,专栏下的代码我全部是自己手动实现了一遍 还用上了gdb很开心 很满足 第27讲和第24讲 应该重点学习,这两讲都是很重要的理论基础

    作者回复: 你是问题最多的,我想也是收获最大的。 调试、调试、调试,重要的问题讲三遍 :)

    2019-10-15
    5
  • CPP
    C语言要是没两把刷子,买了也是浪费钱。再调试也得建立再看懂的基础上......

    作者回复: C语言应该是大学期间必修课程吧.......

    2020-08-10
    3
  • herongwei
    这篇文章,多了很多生动的图片,感觉干货满满啊,哈哈,希望后面的课程,也能多加点对应的图片就更好了。

    作者回复: 我想应该是可以满足你的要求的:)

    2019-10-24
    2
  • supermouse
    思考题第一道:https://github.com/YoungYo/yolanda/blob/master/chap-27/poll-server-onethread-homework.c 这是修改后的代码 思考题第二道: onMessage 方法就是处理 decode-compute-encode 逻辑的吧?

    作者回复: 是的。

    2020-02-25
    1
  • 阿卡牛
    使用多线程有线程池,使用多进程有进程池吗?

    作者回复: 没有。

    2019-11-19
    2
    1
收起评论
显示
设置
留言
43
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部