1. 更好地利用CPU
想象一个程序读取和处理文件,读取文件需要5秒,处理文件需要2秒,现在我们需要读取并处理2个文件:
5秒读取文件A
2秒处理文件A
5秒读取文件B
2秒处理文件B
总共需要14秒
当从磁盘读取文件时,大多数时间都耗费在从磁盘读取文件个工作上,CPU在这段读取文件时间里十分空闲,它应该去做一些事情。我们通过改变操作顺序,让CPU更好地被利用:
5秒读取文件A
5秒读取文件B+2秒处理文件A(此处读取文件B时CPU是空闲的,因此可以让CPU处理文件A)
2秒处理文件B
总共12秒
CPU等待读取了文件A,当开始读取文件B的时候,此时只是IO处理,CPU是空闲的,因此可以处理文件A。
当CPU在等待IO操作时,可以去处理其他任务。当然,不一定是磁盘IO,也可能是网络IO,也可以是来自机器上的输入。网络和磁盘IO比CPU和内存IO慢得多。
2. 更简单的程序设计
如果使用单线程编写上述读取和处理顺序,必须跟踪每个文件的读取和处理状态。若是使用两个线程,每个线程只读取和处理一个文件,等待磁盘读取其文件时,每个线程都会被阻塞,在等待时候,其他线程可以使用CPU来处理他们已经读取的文件。结果是,磁盘一直处于忙碌的状态,将各种文件读进内存。每个线程只需要跟踪一个文件。
3. 更具响应性
设想一个服务器应用程序在某个端口监听传入的请求,当收到请求时,它会处理请求并返回监听,如下所示:
while (server is active) {
listen for request
process request
}
若请求需要很长时间,则在此期间没有新的客户端可以向服务器发送请求,只有在服务器监听时才能发送请求。
另一种设计是监听线程将请求传递给工作线程,然后立即返回监听。工作线程将处理请求并向客户端发送回复。该设计概述如下:
while (server is active) {
listen for request
hand request to worker thread
}
这样服务器线程就可以更快地恢复监听。因此,更多的客户端可以向服务器发送请求,服务器的响应能力增强了。
4. CPU资源更公平地分配
假设一个服务器正在接收来自客户端的请求,想象一下,其中一个客户端发送了一个需要很长时间才能处理的请求,例如10秒。如果服务器使用一个线程处理所有任务,那么请求之后处理缓慢的所有请求都将被迫等待,直到处理完完整的请求。
通过在多个线程之间分配CPU时间并在线程之间切换,CPU可以在多个请求之间更公平地共享其执行时间。假设其中一个请求很慢,处理速度更快的其他请求也可以与速度较慢的请求同时执行。当然,这意味着执行慢请求的速度会更慢。但是,其他请求只需等待更短的时间就能被处理,因为它们不必等到慢任务完成后才能被处理。如果只有慢请求需要处理,那么CPU仍然可以单独分配给慢任务。