27 | I/O多路复用遇上线程:使用poll单线程处理所有I/O事件
27 | I/O多路复用遇上线程:使用poll单线程处理所有I/O事件
讲述:冯永吉
时长08:37大小7.90M
重温事件驱动
基于事件的程序设计: GUI、Web
几种 I/O 模型和线程模型设计
fork
pthread
single reactor thread
single reactor thread + worker threads
样例程序
样例程序结果
总结
思考题
赞 11
提建议
精选留言(42)
- 林林2019-11-26worker thread 和 reactor thread之间怎么进行数据传递?是要利用队列+锁吗?
作者回复: 这就是两个普通的producer-consumer线程关系,使用队列+锁的方式是一个比较常见的实现方式。
13 - 钱2019-11-241:事件驱动模型的设计思想是啥? 事件驱动模型的设计的思想是,一个无限循环的事件分发线程在后台运行,一旦做了某种操作触发了一个事件,这个事件就会被放置到事件队列中,事件分发线程的任务,就为这个发生的事件找到对应的事件回调函数并执行它。 这里有个疑问,事件分发线程怎么找到事件的回调函数,并调用它的? 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+线程池,采用了更加先进的事件驱动设计思想,资源占用少、效率高、扩展性强,是支持高性能高并发场景的利器。展开
作者回复: 第一个问题,回调函数和套接字对应的,通过套接字找到对应的回调函数; 第二个问题,因为是事件驱动,不需要分配固定的资源,仅仅使用几个线程就可以支持上万的连接,每个线程的利用率得到了最大提升。
共 4 条评论11 - fxzhang2019-10-09老师可否讲解linux下如何开发的,最近想换工作,但是之前都在windows下面开发,想自学一下linux下是如何开发的,但是有一种找不到开头不知道该怎么学习的感觉,很无力
作者回复: 先学习一下Linux下的安装、配置、管理,把工作环境放到Linux下面,让Linux成为你的工作效率机器; 其次,慢慢学习Bash,感受一下Linux的能力; 接下来就是学习一些 Linux下的程序设计,如I/O、网络等。 如果你能把这篇系列的所有代码都改一遍,运行一遍,就是一个良好的开头。 加油~
10 - 刘忽悠2020-07-01没太理解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线程是一个非常重要的"大脑",尽量不要让它成为瓶颈。
共 3 条评论5 - 传说中的成大大2019-10-15第一遍看完这篇文章 我就感受颇深 尤其是事件触发 这个模式 然后就想到工作当中的用到的skynet框架底层就是采用事件驱动,某个服务有数据达到 就去触发对应的服务,然后再回想工作当中很多逻辑都抽象成事件,通过一个主循环检测时间然后来触发对应的事件! 更重要的一点,专栏下的代码我全部是自己手动实现了一遍 还用上了gdb很开心 很满足 第27讲和第24讲 应该重点学习,这两讲都是很重要的理论基础
作者回复: 你是问题最多的,我想也是收获最大的。 调试、调试、调试,重要的问题讲三遍 :)
4 - CPP2020-08-10C语言要是没两把刷子,买了也是浪费钱。再调试也得建立再看懂的基础上......
作者回复: C语言应该是大学期间必修课程吧.......
3 - herongwei2019-10-24这篇文章,多了很多生动的图片,感觉干货满满啊,哈哈,希望后面的课程,也能多加点对应的图片就更好了。
作者回复: 我想应该是可以满足你的要求的:)
2 - heyman2020-04-18reactor 线程无限循环,有点像轮询,效率不会很低吗?
作者回复: 不会。这里不是轮询哦,轮询是消耗cpu时间的,这里是系统提供的事件驱动,看似在无限循环,其实这个时候cpu被调度干其他事了,当真正有事件发生,cpu又会被切换回来,所以效率很高。
2 - supermouse2020-02-25思考题第一道:https://github.com/YoungYo/yolanda/blob/master/chap-27/poll-server-onethread-homework.c 这是修改后的代码 思考题第二道: onMessage 方法就是处理 decode-compute-encode 逻辑的吧?
作者回复: 是的。
1 - 阿卡牛2019-11-19使用多线程有线程池,使用多进程有进程池吗?
作者回复: 没有。
共 2 条评论1 - 徐凯2019-10-22我看到别人的代码用到了老师说的这个思想,在接收消息它采用的是分发订阅模式 通过订阅者的回调来接收消息 没有特定的recv接口露给外界 这种设计思想老师怎么看
作者回复: 这是框架设计的基本思想,我在后面的框架设计中也是这样的,欢迎继续阅读,你会有更多收获。
1 - 传说中的成大大2019-10-15第二问根据工作中遇到的情况来看decode-compute-encode应该是业务逻辑,应该是工作线程上处理,觉得处理方式是每个描述符和每个线程做一个映射,并且注册一个回调函数,用这个函数来处理套接字收到消息事件,然后再执行decode-compute-encode逻辑1
- 传说中的成大大2019-10-15之前由于忙着买房子 落下了很多课程 现在都在追,不过不管是追还是慢慢跟 我都会再好好复习的
作者回复: 加油~
共 4 条评论1 - Running man2022-08-26 来自浙江这个线程数为零的时候,threadPool动态申请了一个线程,但实际并没有使用,也没有释放是不是会造成资源泄露?
- 德鲁小叔2022-01-31Synchronous Event Demultiplexer(同步事件分离器) 是这个吗
作者回复: 这个名词,看起来很熟系......
- 德鲁小叔2022-01-31不会。这里不是轮询哦,轮询是消耗cpu时间的,这里是系统提供的事件驱动,看似在无限循环,其实这个时候cpu被调度干其他事了。 老师,这里系统提供的事件驱动是指什么?既然不是无限轮训,那是怎么样执行呢?
作者回复: 说的通俗一点,就是操作系统帮我们把这个事情给干了,操作系统是个多面手,可以干干这个,也干干那个,等到有事件发生的时候,就会腾出手来帮我们处理网络I/O事件了。
- 德鲁小叔2022-01-30有一个问题,有一个无线轮询的线程会对CPU的消耗有影响吗?
作者回复: 当然,你可以试试哦。
- 菜鸡互啄2021-12-22老师你好 这边开始有点晕了。代码里贴的event_loop反应堆就是基于系统轮询(如slect/poll/epoll)+非阻塞封装的吗
作者回复: 基于I/O多路复用+非阻塞。一般的,都把select/poll/epoll叫做I/O多路复用。
- S2021-08-18请问老师,你程序第一句打印add channel fd == 4, main thread,为什么第一个fd是4而不是3? 0,1,2分别代表输入,输出,错误。那么fd:3被谁占用了?
作者回复: 好问题。 是这样的,fd:3其实是被socketpair占了,也就是说fd:4是主reactor占用的描述字,fd:3是这个主reactor对应的socketpair,这是为了让反应堆线程能够随时感知新的事件。
共 2 条评论 - coder2020-11-08netty也是用的reactor模型
作者回复: 是的。