尽管多线程编程很有挑战,但它也有自己的优势,所以仍然使用多线程技术。下面是多线程的一些优势:

  • 更好的资源利用率
  • 在某些情况下能简化程序设计
  • 高响应程序

更好的资源利用率

假设一个应用程序在本地文件系统中读取并处理文件。从磁盘中读取文件需要消耗 5 秒的时间,然后处理这个文件需要消耗 2 秒的时间。处理两个文件需要消耗:

5 秒读文件 A
   2 秒处理文件 A
   5 秒读文件 B
   2 秒处理文件 B
-----------------------
   总共消耗 14 秒

在磁盘读取文件的时候,大部分的 CPU 时间都消耗在等待磁盘读取数据的过程中。在这个过程中, CPU 大部分处于空闲状态,其实这个空闲时间是可以被利用起来的。通过改变操作的顺序, CPU 可以被更好的利用。请看下面的操作顺序:

5 秒读文件 A
   5 秒读文件B 同时 2 秒处理文件 A
   2 秒处理文件 B
-------------------------
   总共消耗 12 秒

CPU 会等待第一个文件读取就绪,第一个文件读取完毕以后, CPU 会向磁盘发出读取第二个文件的指令。在磁盘读取第二个文件的同时, CPU 处理第一个文件。要明白,磁盘在读取第一个文件的时候, CPU 大部分处于空闲状态(这部分的 CPU 时间没办法被利用)。

通常, CPU 在等待 IO 的同时可以做其他的事。这个 IO 不一定是磁盘 IO ,它也可以是网络 IO 或者是来自用户的一个输入。网络和磁盘 IO 通常要比 CPU’s 和内存 IO 慢很多。

磁盘是一种资源, CPU 也是一种资源,在磁盘工作的同时, CPU 如果也能工作就提高了资源的利用率。

简化程序设计

如果你通过手工编程的方式在一个单线程应用程序中按照上面的顺序读取和处理文件,你还必须得跟踪每个文件的读取和处理状态。你可以启动两个线程来代替这种手工编程的方式,每个线程负责读取和处理一个文件。每个线程在等待磁盘读取自己的文件的时候,都会被阻塞。一个线程在等待磁盘读取文件的同时如果另外一个线程的文件已经读取完毕了,这时候另外一个线程可以利用 CPU 处理部分文件。结果就是,磁盘一直处于忙碌状态,把各种文件都读取到内存中。这就提高了磁盘和 CPU 的利用率。由于每个线程只跟踪一个文件,这种情况编程也更容易实现。

高响应程序

把单线程应用程序改为多线程应用程序的另外一个常见的目的是为了实现高响应的应用程序。假如一个服务器应用程序在某个端口上监听输入请求。当收到一个请求以后,先处理这个请求然后再返回继续监听。一个服务器循环骨架如下:

while (server is active) {
    listen for request
    process request
}

如果一个请求得占用很长的时间来处理,在处理这个请求的过程中,任何新的客户端向服务器发送的请求都得不到响应。只有服务器处于监听状态时(listen for request)请求才会被服务器接收到。

另外一种设计是:监听线程把请求传递给工作线程,然后监听线程立即返回并继续监听客户端发来的请求。工作线程将会处理这个请求然后给客户端一个响应。这种设计的骨架如下:

while (server is active) {
    listen for request
    hand request to worker thread
}

这种方式服务器监听线程将会很快地返回并继续监听。因此,更多的客户端可以向服务器发送请求,服务器的响应也更高。

对桌面应用程序也是成立的,如果你点了一个按钮,启动了一个很耗时的任务。线程先执行任务然后再更新窗体、按钮等。在线程执行任务的时候,这个桌面应用程序会出现无法响应的状态。为了解决这个问题,可以把这个待处理的任务传递给一个工作线程来处理。在工作线程忙着处理任务的同时,窗体线程可以自由地响应用户的请求。当工作线程处理完任务后它会通知窗体线程。然后窗体线程可以根据这个任务完成的结果来更新应用程序窗体。使用了工作线程设计的应用程序将会提高对用户响应的速度。