我看到这是一个古老的问题,但我认为这里遗漏了一些东西,@nickdu试图指出但还不是很清楚。
有四种类型的 IO 与此讨论相关:
阻止 IO
非阻塞 IO
异步 IO
异步非阻塞 IO
我认为这种混淆是因为定义含糊不清。因此,让我试着澄清这一点。
首先,我们来谈谈 IO。当我们有慢速IO时,这是最明显的,但IO操作可以是阻塞的,也可以是非阻塞的。这与线程无关,它与操作系统的接口有关。当我要求操作系统进行IO操作时,我可以选择等待所有数据准备就绪(阻止),或者获取现在可用的数据并继续前进(非阻塞)。默认值为阻止 IO。使用阻塞 IO 编写代码要容易得多,因为路径更清晰。但是,您的代码必须停止并等待 IO 完成。非阻塞 IO 需要与较低级别的 IO 库进行交互,使用选择和读/写,而不是使用提供方便操作的较高级别的库。非阻塞 IO 还意味着在操作系统执行 IO 时,您有一些需要处理的内容。这可能是对已完成的 IO 执行的多个 IO 操作或计算。
阻止 IO - 应用程序等待操作系统收集所有字节以完成操作或到达末尾,然后再继续。这是默认设置。为了更清楚地说明技术性,启动 IO 的系统调用将安装一个信号处理程序,等待 IO 操作进行时将发生的处理器中断。然后,系统调用将开始休眠,该休眠将当前进程的操作暂停一段时间,或直到进程中断发生。
非阻塞 IO - 应用程序告诉操作系统它只想要哪些字节现在可用,并在操作系统同时收集更多字节时继续前进。该代码使用 select 来确定哪些 IO 操作具有可用的字节。在这种情况下,系统调用将再次安装信号处理程序,但不会休眠,而是将信号处理程序与文件句柄相关联,并立即返回。该进程将负责定期检查已设置的中断标志的文件句柄。这通常通过选择呼叫来完成。
现在,异步是混乱开始的地方。异步的一般概念仅意味着在执行后台操作时该过程继续,发生这种情况的机制并不具体。该术语是模棱两可的,因为非阻塞 IO 和线程阻塞 IO 都可以被视为异步的。两者都允许并发操作,但是资源要求不同,并且代码也大不相同。因为您已经问了一个问题“什么是非阻塞异步 IO”,所以我将对异步使用更严格的定义,异步是执行 IO 的线程系统,它可能是非阻塞的,也可能不是非阻塞的。
一般定义
异步 IO - 允许发生多个并发 IO 操作的编程 IO。IO 操作同时发生,因此代码不会等待未准备好的数据。
更严格的定义
异步 IO - 使用线程或多处理来允许发生并发 IO 操作的编程 IO。
现在有了这些更清晰的定义,我们有以下四种类型的IO范例。
阻塞 IO - 标准单线程 IO,其中应用程序等待所有 IO 操作完成,然后再继续。易于编码,没有并发性,对于需要多个 IO 操作的应用程序来说速度很慢。进程或线程将在等待 IO 中断发生时进入睡眠状态。
异步 IO - 线程化 IO,其中应用程序使用执行线程并发执行阻塞 IO 操作。需要线程安全代码,但通常比其他代码更易于读取和写入。获得多个线程的开销,但具有清晰的执行路径。可能需要使用同步的方法和容器。
非阻塞 IO - 单线程 IO,其中应用程序使用 select 来确定哪些 IO 操作已准备好推进,从而允许在操作系统处理并发 IO 时执行其他代码或其他 IO 操作。进程在等待 IO 中断时不会休眠,但负责检查文件句柄上的 IO 标志。由于需要使用select检查IO标志,因此代码要复杂得多,尽管不需要线程安全代码或同步方法和容器。低执行率,以牺牲代码复杂性为代价。执行路径很复杂。
异步非阻塞 IO - 一种混合的 IO 方法,旨在通过使用线程来降低复杂性,同时尽可能通过使用非阻塞 IO 操作来保持可伸缩性。这将是最复杂的 IO 类型,需要同步的方法和容器,以及复杂的执行路径。这不是人们应该考虑轻易编码的IO类型,并且通常仅在使用会掩盖复杂性的库时使用,例如Futures和Promise。