尽管面临种种挑战,多线程仍被使用是因为如下的一些好处:
- 更好的资源利用性。
- 在某些情况下更简单的程序设计。
- 程序的响应性更强。
更好的资源利用性
设想一个从本地文件系统读取和处理文件的应用程序。假设从磁盘读取一个文件需要5秒,处理它需要2秒。然后处理两个文件需要:
5 seconds reading file A 2 seconds processing file A 5 seconds reading file B 2 seconds processing file B ----------------------- 14 seconds total
5 seconds reading file A 5 seconds reading file B + 2 seconds processing file A 2 seconds processing file B ----------------------- 12 seconds total
一般来说,CPU可以在等待IO时做其他事情。它不必是磁盘IO。它也可以是网络IO,或者从机器上的用户输入。网络和磁盘IO通常比CPU和内存IO慢得多。
更简单的程序设计
如果要在单线程应用程序中手动编写上述读取和处理顺序,你得跟踪每个文件的读取和处理状态。相反,你可以启动两个线程,每个线程只读取和处理单个文件。在等待磁盘读取其文件时,这些线程中的每一个都将被阻塞。在等待时,其他线程可以使用CPU来处理他们已经读取的文件的部分。这样的话磁盘始终保持忙碌,从各种文件读入内存。这使得磁盘和CPU更好被利用。因为每个线程只需要跟踪单个文件,编程起来也容易。
程序的响应性更强
将单线程应用程序转换为多线程应用程序的另一个常见目标是实现更灵敏的应用程序。设想一个服务器应用程序监听某些端口上的请求。当接收到请求时,它处理该请求,然后回到监听状态。服务器循环如下所示:
while(server is active){ listen for request process request }
如果请求需要很长时间进行处理,则没有新的客户端可以在该持续时间内向服务器发送请求。 只有在服务器正在监听时才能接收请求。
另一种设计是监听线程将请求传递给工作线程,并立即返回监听。 工作线程将处理请求,并向客户端发送回复。 这个设计草图如下:
while(server is active){ listen for request hand request to worker thread }
这样服务器线程将可以更快地回到监听状态。 因此,更多的客户端可以向服务器发送请求,服务器也因此变得更加灵敏。
桌面应用程序也是如此。 如果单击一个按钮来启动一个长任务,并且执行该任务的线程是更新窗口,按钮等的线程,那么应用程序将在任务执行时无响应。 相反,任务可以切换到工作线程。 当工作线程忙于任务时,窗口线程可以自由响应其他用户请求。 当工作线程完成时,它发信号通知窗口线程。 然后窗口线程可以用任务的结果更新应用程序窗口。 具有工人线程(worker thread)设计的程序对用户更有响应性响。
while(server is active){ listen for request hand request to worker thread }