为什么CPU密集型任务使用阻塞I/O更好,而I/O密集型任务则适于非阻塞I/O?

11
我被告知对于I/O密集型应用程序,非阻塞I/O会更好。对于CPU密集型应用程序,阻塞I/O要好得多。我找不到这种说法的原因。尝试了Google,但很少有文章涉及此主题并且没有太多详细信息。是否有人能为此提供深入的原因?
同时,我想弄清楚非阻塞I/O的缺点是什么。
在查看另一个线程之后,在这里,我能够理解的一种原因是,只有当I/O进程足够繁重时,我们才能看到使用非阻塞I/O的显着性能提升。它还指出,如果I/O操作的数量很大(典型的Web应用程序场景),其中有许多请求寻找I/O请求,那么我们也可以使用非阻塞I/O来实现显着的性能提升。
因此,我的问题归结为以下列表:
1. 对于CPU密集型应用程序,最好启动线程池(或scala的executionContext)并将工作分配给线程池的线程。(我认为这肯定比自己生成线程并手动划分工作有优势。此外,使用Future的异步概念,即使是CPU密集型工作,也可以使用回调返回,从而避免与多线程阻塞相关的问题?)。如果有足够快速的I/O,则可以在线程池本身的线程上使用阻塞原则进行I/O。我是对的吗?
2. 非阻塞I/O技术上的缺点或开销是什么?为什么如果I/O足够快或需要的I/O操作很少,我们不会看到使用非阻塞I/O的性能提升?最终是操作系统处理I/O。无论I/O的数量是大还是小,让操作系统处理疼痛会有什么区别。那么这里有什么不同之处呢?

3
只有在等待IO时有其他事情可做时(例如处理其他连接),非阻塞IO才会更好。 - Colonel Thirty Two
1
嗯,我能理解为什么阻塞 I/O 可能足够(因为你无论如何都不能做其他有用的事情),但我不明白为什么它会更好(至少从性能的角度来看,这可能对程序员来说更容易)。 - Thilo
您阻塞I/O的理由是什么,这个原因足够充分吗? - piyushGoyal
2
非阻塞I/O释放CPU线程以处理其他事务。如果没有其他任务需要处理(或者只有很少,使用几个线程也不会导致太大的开销),那么采用非阻塞方式并不能带来太多好处。 - Thilo
完全有道理。现在的问题是:非阻塞I/O相关的开销是什么? - piyushGoyal
显示剩余6条评论
1个回答

15
从程序员的角度来看,阻塞I/O比非阻塞I/O更容易使用。你只需调用读/写函数,当它返回时就完成了。而对于非阻塞I/O,您需要检查是否可以读/写,然后读/写,然后检查返回值。如果没有全部读取或写入,您需要机制再次读取或在稍后写入。
关于性能:一个线程中的非阻塞I/O并不比一个线程中的阻塞I/O更快。I/O操作的速度由读取或写入的设备(例如硬盘)决定。速度不是由等待(阻塞)或不等待(非阻塞)的人决定的。而且,如果您调用阻塞I/O函数,那么操作系统可以有效地进行阻塞。如果您需要在应用程序中进行阻塞/等待,则可能与操作系统几乎一样好,但也可能做得更差。
因此,为什么程序员会让自己的生活变得更加艰难,并实现非阻塞I/O呢?因为,这是关键,他们的程序不仅需要执行单个I/O操作。使用阻塞I/O时,您需要等待阻塞I/O完成。使用非阻塞I/O时,您可以在阻塞I/O完成之前进行一些计算。当然,在非阻塞I/O期间,您也可以触发其他I/O(阻塞或非阻塞)。
非阻塞I/O的另一种方法是投入更多的线程来进行阻塞I/O,但正如您链接的SO帖子中所说,线程是有成本的。这个成本比(操作系统支持的)非阻塞I/O的成本还要高。
如果您有一个大量I/O但CPU使用率很低的应用程序,例如具有大量并行客户端的Web服务器,则使用少量带有非阻塞I/O的线程。使用阻塞I/O会导致大量线程->高成本,因此只使用少量线程->需要非阻塞I/O。
如果你有一个需要大量CPU计算的应用程序,比如读取文件、对所有数据进行复杂的计算并将结果写入文件,则99%的时间都将花费在CPU密集型部分。因此,创建一些线程(例如每个处理器一个线程),可以并行处理尽可能多的计算。对于I/O,您可能会使用一个主要线程来处理阻塞I/O,因为它更容易实现,并且主线程本身没有其他并行任务可执行(假设计算在其他线程中完成)。
如果您有一个需要大量CPU和I/O资源的应用程序,那么您需要使用一些线程和非阻塞I/O。可以将其想象为具有许多客户端和网页请求的Web服务器,在CGI脚本中进行密集计算。在等待连接上的I/O时,程序可以计算另一个连接的结果。或者考虑一个读取大型文件并对文件块进行密集计算(如计算平均值或将所有值加1)的程序。在这种情况下,您可以使用非阻塞读取,并在等待下一次读取完成时已经可以计算可用数据。如果结果文件只是一个小的压缩值(如平均值),则可以使用阻塞写入结果。如果结果文件与输入文件一样大,像“所有值+1”一样,则可以使用非阻塞写入结果,并在写入过程中自由地对下一个块进行计算。

感谢 Werner Henze 提供如此详细的解释。:-) 有一个快速的问题:“在一个线程中使用非阻塞 I/O 并不比在一个线程中使用阻塞 I/O 更快。”为什么会这样呢?你能指导我一些网址或其他东西吗?我总体上理解了你的概念和精彩的解释。我的下一个显而易见的问题与我问过你的相同。干杯! - piyushGoyal
@piyushGoyal 我更新了我的回答并添加了:“I/O 操作的速度取决于读取或写入的设备(例如硬盘)。速度不是由等待(阻塞)或不等待(非阻塞)它的人确定的。” - Werner Henze
@WernerHenze -因此,创建一些线程(例如每个处理器一个)并并行执行尽可能多的计算。关于I/O,您可能会坚持使用阻塞式I/O,因为它更容易实现,并且程序在并行执行时没有其他事情可做。我不理解这个。第一部分说要并行执行它们(使用多线程/协程和/或多进程)。最后一部分说程序在并行执行时没有其他事情可做。感谢您的详细解释。真的很有帮助。 - technazi
@technaz 感谢您的反馈。我重写了这段话,希望现在更加清晰明了。 - Werner Henze

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接