非阻塞与异步

非阻塞与异步

在标准的网络服务器的构建中,IO 模式会按照 Blocking/Non-Blocking、Synchronous/Asynchronous 这两个标准进行分类,其中 Blocking 与 Synchronous 大同小异,而 NIO 与 Async 的区别在于 NIO 强调的是轮询(Polling),而 Async 强调的是通知(Notification)。譬如在一个典型的单进程单线程 Socket 接口中,阻塞型的接口必须在上一个 Socket 连接关闭之后才能接入下一个 Socket 连接。而对于 NIO 的 Socket 而言,服务端应用会从内核获取到一个特殊的 “Would Block” 错误信息,但是并不会阻塞到等待发起请求的 Socket 客户端停止。

非阻塞与异步对比

一般来说,在 Linux 系统中可以通过调用独立的 select 或者 epoll 方法来遍历所有读取好的数据,并且进行写操作。而对于异步 Socket 而言(譬如 Windows 中的 Sockets 或者 .Net 中实现的 Sockets 模型),服务端应用会告诉 IO Framework 去读取某个 Socket 数据,在数据读取完毕之后 IO Framework 会自动地调用你的回调(也就是通知应用程序本身数据已经准备好了)。以 IO 多路复用中的 Reactor 与 Proactor 模型为例,非阻塞的模型是需要应用程序本身处理 IO 的,而异步模型则是由 Kernel 或者 Framework 将数据准备好读入缓冲区中,应用程序直接从缓冲区读取数据。

  • 同步阻塞:在此种方式下,用户进程在发起一个 IO 操作以后,必须等待 IO 操作的完成,只有当真正完成了 IO 操作以后,用户进程才能运行。

  • 同步非阻塞:在此种方式下,用户进程发起一个 IO 操作以后边可返回做其它事情,但是用户进程需要时不时的询问 IO 操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的 CPU 资源浪费。

  • 异步非阻塞:在此种模式下,用户进程只需要发起一个 IO 操作然后立即返回,等 IO 操作真正的完成以后,应用程序会得到 IO 操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的 IO 读写操作,因为真正的 IO 读取或者写入操作已经由内核完成了。

而在并发 IO 的问题中,较常见的就是所谓的 C10K 问题,即有 10000 个客户端需要连上一个服务器并保持 TCP 连接,客户端会不定时的发送请求给服务器,服务器收到请求后需及时处理并返回结果。

Links