处理并发:异步非阻塞

 

这是借鉴博客内容:    node

在研究nginx和Node.js的时候常会遇到异步、非阻塞等,以前本身也常用epoll,对其同步与阻塞,异步与非阻塞有了必定的认识,现对参考资料总结下。nginx

    首先讨论下使用事件驱动,异步编程的优势:编程

    充分利用了系统资源,执行代码无须阻塞等待某种操做完成,有限的资源能够用于其余的任务。其很是适合于后端的网络服务编程。后端

    在服务器开发中,并发的请求处理是个大问题,阻塞式的函数会致使资源浪费和时间延迟。经过事件注册、异步函数,开发人员能够提升资源的利用率,性能也会改善。其nginx和node.js处理并发都是采用的事件驱动异步非阻塞模式。其中nginx中处理并发用的是epoll,poll,queue等方式,node.js使用的是libev,它们对大规模的HTTP请求处理的都很好。服务器

阻塞

    《node.js开发指南》是这样定义的:线程在执行中若是遇到(I/O 操做)如磁盘读写或网络通讯,一般要耗费较长的时间,这时操做系统会剥夺这个线程的 CPU 控制权,使其暂停执行,同时将资源让给其余的工做线程,这种线程调度方式称为 阻塞。当 I/O 操做完毕时,操做系统将这个线程的阻塞状态解除,恢复其对CPU的控制权,令其继续执行。这种 I/O 模式就是一般的同步式 I/O(Synchronous I/O)或阻塞式 I/O(Blocking I/O)。网络

非阻塞

    非阻塞是这样定义的,当线程遇到 I/O 操做时,不会以阻塞的方式等待 I/O 操做的完成或数据的返回,而只是将 I/O 请求发送给操做系统,继续执行下一条语句。当操做系统完成 I/O 操做时,以事件的形式通知执行 I/O 操做的线程,线程会在特定时候处理这个事件。多线程

对比阻塞与非阻塞

    阻塞模式下,一个线程只能处理一项任务,要想提升吞吐量必须经过多线程。并发

    非阻塞模式下,一个线程永远在执行计算操做,这个线程所使用的 CPU 核心利用率永远是 100%,I/O 以事件的方式通知。异步

    在阻塞模式下,多线程每每能提升系统吞吐量,由于一个线程阻塞时还有其余线程在工做,多线程可让 CPU 资源不被阻塞中的线程浪费。异步编程

    而在非阻塞模式下,线程不会被 I/O 阻塞,永远在利用 CPU。多线程带来的好处仅仅是在多核 CPU 的状况下利用更多的核。

 

    来看看《深刻浅出Node.js》对异步I/O的解释,在操做系统中,程序运行的空间分为内核空间和用户空间。咱们经常提起的异步I/O,其实质是用户空间中的程序不用依赖内核空间中的I/O操做实际完成,便可进行后续任务。

    I/O的阻塞与非阻塞的解释

    阻塞模式的I/O会形成应用程序等待,直到I/O完成。同时操做系统也支持将I/O操做设置为非阻塞模式,这时应用程序的调用将可能在没有拿到真正数据时就当即返回了,为此应用程序须要屡次调用才能确认I/O操做彻底完成。

 

    I/O的同步与异步I/O的同步与异步出如今应用程序中。若是作阻塞I/O调用,应用程序等待调用的完成的过程就是一种同步情况。相反,I/O为非阻塞模式时,应用程序则是异步的。

 

    参照《node.js入门经典》中对同步的解释,同步的代码意味着每一次执行一个操做,在一个操做完成以前,代码的执行会被阻塞,没法移到下一个操做上。也就是说代码的执行会在函数返回前中止。直到函数返回后,代码才会继续执行。

相反,异步就意味着函数的执行无需等待某个操做的结果就能够继续执行,其操做的结果会在事件发生时由回调来处理。

 

异步I/O优缺点

    使用同步IO,它的优势是可使程序调试方便,可是它的缺点也是明显的,程序的执行过程当中若是入到一些耗时的IO操做,程序的执行都要等待该IO的完成,在这个等待的过程当中,程序没法充分利用CPU,致使了CPU的闲置,为了充分利用CPU,和IO并行操做,经常使用的方法有2中:

    (1)多线程单进程

    多线程的设计之处就是为了在共享的程序空间中,实现并行处理任务,从而达到充分利用CPU的效果。

    多线程缺点:

    其1、执行时(线程切换)上下文交换的开销较大,一个线程大约须要2M的内存空间,占用资源较大。

    其2、状态同步(锁)的问题,它也使得程序的编写和调用复杂化。

    (2)单线程多进程

    为了不多线程形成的使用不便问题,有的语言选择了单线程保持调用简单化,采用启动多进程的方式来达到充分利用CPU和提高整体的并行处理能力。它的缺点在于业务逻辑复杂时(涉及多个I/O调用),由于业务逻辑不能分布到多个进程之间,事务处理时长要远远大于多线程模式。

异步I/O与轮询技术

    当进行非阻塞I/O调用时,要读到完整的数据,应用程序须要进行屡次轮询,才能确保读取数据完成,以进行下一步的操做。轮询技术的缺点在于应用程序要主动调用,会形成占用较多CPU时间片,性能较为低下。现存的轮询技术有如下这些: read、select、poll、epoll、pselect、kqueue 

 

    read是性能最低的一种,它经过重复调用来检查I/O的状态来完成完整数据读取。

    select是一种改进方案,经过对文件描述符上的事件状态来进行判断。

    操做系统还提供了poll、epoll等多路复用技术来提升性能。

    轮询技术知足了异步I/O确保获取完整数据的保证。可是对于应用程序而言,它仍然只能算时一种同步,由于应用程序仍然须要主动去判断I/O的状态,依旧花费了不少CPU时间来等待。上一种方法重复调用read进行轮询直到最终成功,用户程序会占用较多CPU,性能较为低下。而实际上操做系统提供了select方法来代替这种重复read轮询进行状态判断。select内部经过检查文件描述符上的事件状态来进行判断数据是否彻底读取。可是对于应用程序而言它仍然只能算是一种同步,由于应用程序仍然须要主动去判断I/O的状态,依旧花费了不少CPU时间等待,select也是一种轮询。

理想的异步I/O模型

    理想的异步I/O应该是应用程序发起异步调用,而不须要进行轮询,进而处理下一个任务,只需在I/O完成后经过信号或是回调将数据传递给应用程序便可。

相关文章
相关标签/搜索