本文节选自BitTiger CS503 全栈开发实战精品课,5月18日正式开课!报名最后五天截止!点击阅读原文马上预约咨询!
Node.js有一个最重要的概念就是单线程异步IO。也许你心中也曾嘀咕过很疑惑,如果是单线程那怎么实现异步操作,单线程谁去响应事件?今天我们就来谈谈这一点,解开这个美丽的误会。
首先明确一点虽然我们在学习操作系统和考试的时候是区分线程和进程的,但在很多情况下我们可以把它们当作一回事看待。我们的操作系统很明显是支持多进程的,比如我用虾米音乐听着歌的同时,写下了这篇文章,后台还挂着微信和QQ。
经过多年发展,我们使用的CPU发展到了多核,但在最初的时候,CPU都是单核的,所以如果要支持多进程,就必须采用CPU分时(Time Sharing)技术,每个进程都轮流执行一定时间,做到“雨露均沾”。当然,由于CPU执行速度快和每个分时片段时间极短,让用户觉得好像每个进程都是一直在执行的。
如下图所示,很多传统Web服务器沿袭了操作系统的设计模式,也都是支持多线程(多进程)。
虽然实现了多线程的支持,但又出现了新的问题——上下文切换耗时。由于执行时间片段有限,每个进程可能到时间到了以后还没有执行完,但又必须把CPU使用权限交出去,那就只有把中间结果保存到寄存器中,直到下一次轮到又要从寄存器中读取出中间结果,反复如此。而我们也知道这种读写操作相对于CPU计算,速度是差了好几个数量级的,像这样就把大量时间花在I/O上很划不来。
那我们想一下操作系统由于它的特性,必须支持多线程,但一般的Web服务器并不是这样。对于Web服务器而言,任何请求都可以按照先来后到的原则进行处理。所以把Web服务器设计成单线程也并没有什么问题。
把每个请求处理完了再处理下一个,理论上可行,但事实上有很大的问题。每个请求除了CPU计算,还可能有数据库的读写,网络传输,文件读写等都是耗费时间的I/O操作。当你用单线程在进行“缓慢”的I/O操作时候,CPU就“无所事事”了。
这个问题在多线程中就不存在了,当轮到某个线程执行的时候发现还在I/O操作,就可以暂时跳过这个线程,去下一个线程。这样CPU一直不停运作。
这么看来,好像还是多线程的模式好一些。其实不然,多线程还有一个大问题,就在于线程池的管理,假设现在有100万个请求,其中80万个有I/O操作,轮询到这些线程的时候都会暂时跳过处理,那线程池里面就会有80万个线程存在。管理如此大量的线程,就会变成一个头疼的问题。
针对多线程上下文切换和庞大线程池管理两个问题以及单线程会出现CPU闲置的问题,Node.js提出了一套方案。