一、IO多路复用与多线程
IO多路复用,IO Multiplexing,其实就是在IO上进行监听处理导致线程被阻塞(如果非阻塞就必须不断的轮询,仍然是占用此线程),如果一个IO对应一个线程是不是太浪费了。而且在诸如网络IO这种可以多达成千万的情况下使用多线程对应几乎是一场灾难。所以,可不可以提供一个技术实现在一个线程(进程)同时对多路IO进行监听处理呢?这就是IO多路复用。它的优势非常明显就是解决了线程(进程)膨胀的问题。
这就是说,IO操作是目的,多线程是实现的手段或者说方法。但线程是有上限有代价的,所以,就必须从IO处着手解决这个问题,从而提出了IO多路复用的技术。
二、同步异步与阻塞和非阻塞
说到同步异步这种老生常谈的东西其实非常理解,很多开发者或初学者总想搞清楚某个技术到底是同步还是异步。而某些框架或库在提供给外面文档上说,某某技术是异步的,但明明在书本或者其它网上看到它是同步的,就非常困惑。其实这个可以理解,很多人对框架或库的理解或者应用其实非常浅。并没有深入到其内部进行分析,有可能甚至都没有看过几行代码,都是人云亦云。
包括阻塞和非阻塞这两个概念,很多开发者一直就分不清楚他们和同步异步有什么不同。包括一些老的程序员都会在多线程和IO操作中乱叫,导致新手更是一头雾水。其实可以简单的理解成线程间处理的叫同步或异步,IO的操作称为阻塞或非阻塞。他们之间既可以单独分开使用,也可以组合一起使用。
举一个例子,epoll本身是同步的,但其上层的封装库就可以用线程实现异步的操作。epoll操作的网络句柄Socket虽然一般设置成非阻塞,但并不代表其不可以修改成阻塞。一切都是为效率和应用 。需要重点强调的,socket设置为阻塞和非阻塞并不影响epoll接口(如epoll_wait等)的操作,它影响是和其相关的Accept,Recv,Send等相关的函数接口。
因此,在真正应用一项技术前,要把其相关的接口和底层实现,基本要搞清楚,不要想当然。
三、二者的结合
其实说到这里大家应该明白得差不多了,现实世界中,哪里有什么一个单纯的方法通解决复杂的问题。正如人们吃药,大多数情况下,要吃几种药一起来控制病情。即使某种药是特效的,医生也会嘱咐病人要注意饮食,适当运动等等。这些其实都是一种组合的方式来解决问题的思路。
而在计算机的世界亦也如此。除了一些过小的或过一专项的应用,大多还是需要多种技术整合在一起解决问题。比如网络编程,不同的系统不同的平台可能提供的底层机制就有不同,比如Windows的IOCP,Linux的epoll。它们的底层本身就有不同,而上层的设计更是可以有多种形式。解决问题的思路不止一个,实现思路的手段更是多种,不要强求哪种技术是最牛的。
仍然是那句话,最合适的就是最优的。要立足实际情况,用最简单方便的手段解决问题,就是成功。
四、总结
这篇文章本来不想写的。可是,在网上经常看到有人问这个是异步还是同步,甚至有的还会因为这个互相杠。其实没有意义,是与不是,多看看源码。可能不同的版本,或者同一版本在不同的OS上,都有所不同。不必非要纠结是同步还是异步,达到目的即可。真正想明白同步异步,还是要明白适用的场景和设计的目的以及思想。不要光盯着学习资料或者书本,深入到源码中去,自然就明白了。这正是“从代码中来,到代码中去”。