IO多路复用和线程池

问题本身貌似有问题。简单比较两种工具的优劣意义不大。你没法说锤子和剪刀那个更好。我们一般会评价对于某个场景,哪种工具更合适。

io多路复用(这翻译真的很坑爹啊),指的是同一个进(线)程可以处理多个IO数据流。

多线程

+池模型指的是每个线程处理一个IO流。

IO多路复用的优势在于,当处理的消耗对比IO几乎可以忽略不计时,可以处理大量的并发IO,而不用消耗太多CPU/内存。这就像是一个工作很高效的人,手上一个todo list,他高效的依次处理每个任务。这比每个任务单独安排一个人要节省(雇人是要发工资的……)。典型的例子是nginx做代理,代理的转发逻辑相对比较简单直接,那么IO多路复用很适合。相反,如果是一个做复杂计算的场景,计算本身可能是个 指数复杂度的东西,IO不是瓶颈。那么怎么充分利用CPU或者显卡的核心多干活才是关键。

此外,IO多路复用适合处理很多闲置的IO,因为IO socket的数量的增加并不会带来进(线)程数的增加,也就不会带来stack内存

,内核对象,切换时间的损耗。因此像长链接做通知的场景非常适合。

IO多路复用 + 单进(线)程有个额外的好处,就不会有并发编程的各种坑问题,比如在nginx里,redis里,编程实现都会很简单很多。编程中处理并发冲突和一致性,原子性问题真的是很难,极易出错。

但是现实中,也有IO多路复用 + 多worker线程
的做法,这样上面这个好处就没有了。

如果做不到“处理过程相对于IO可以忽略不计”,IO多路复用的并不一定比线程池方案更好。比如一个web的服务,用jetty 9的NIO connector,后边是spring svc + JDBC连接数据库。spring svc + JDBC连接数据库这两块的处理延迟相对于NIO来说不能忽略,所以并不能指望用jetty 9的NIO connector换了之前的BIO connector的容器,性能能高不少(实际上应该会高一些,但不会太夸张,毕竟瓶颈在后边处理和DB上)。

顺便提一句,Java世界里,因为JDBC这个东西是BIO的,所以在我们常见的Java服务里没有办法做到全部NIO化,必须得弄成多线程模型。如果要在做Java web服务这个大场景下享受IO多路复用的好处,要不就是不需要DB的,要不就是得用Vert.X一类的纯NIO框架把DB IO访问也得框进来。

最后,如果IO压力过大,一个高并发的东西和一个不那么高并发的东西,都不能正确响应,对用户来说是一样的——就是不能用。假如IO非常的繁重,没有空闲的连接,那么IO的压力在两种模型下表现差不多,IO多路复用的“并发“看了起来会大一些,但因为IO已经满了,所以表现出超时严重;而线程池可能表现为,所有的线程都因为IO过慢而卡死了,线程池耗尽,新的请求进不来直接报错。但不管哪一种,在极端压力下,都无法正常工作。这时,要想着怎么扩容。

简单总结一下,

  • 如果压力不是很大,并且处理性能相对于IO可以忽略不计
    • IO多路复用+单进(线)程比较省资源
    • 适合处理大量的闲置的IO
    • IO多路复用+多单进(线)程与线程池方案相比有好处,但是并不会有太大的优势
  • 如果压力很大,什么方案都得跪,这时就得扩容。当然因为IO多路复用+单进(线)程比较省资源,所以扩容时能省钱。

你可以了解下飞机塔台的的工作方式。一个飞空员手上会有很多个航班的牌子。一个人同时管多个牌子,而不是一个人就管1个牌子。如果不能感受到的话,可以看看ACI扩展下知识。

转自:https://www.zhihu.com/question/306267779/answer/570147888

posted @ 2022-03-09 09:45  甜菜波波  阅读(478)  评论(0编辑  收藏  举报