当我们提到线程这个概念的时候,我们就不得不提到它的容器 --- 进程。那什么是进程呢?进程是一个正在运行的程序,它拥有自己的内存地址空间以及其向系统所申请的其它资源。内存地址空间中包含有文本区域( text region )、数据区域( data region )以及栈区域( stack region );所申请的资源包括该进程打开的文件以及套接字等系统资源。当进程被创建并在获得其运行所需要的所有资源过后,进程并不能自己开始执行。现在的进程就像一个植物人一样,由于大脑无法正常的工作,它除了躺在床上之外,是没有办法做其它任何工作的。与人类似,进程在没有获得系统中的 CPU 资源的时候,也只有乖乖的睡在那里。在系统中, CPU 资源是由操作系统中的调度程序进行分配的。调度程序一般采用时间片的方式来分配 CPU 资源,即让一个进程获得一定数量的时间(例如 10ms )片来执行它的代码,在该进程执行完该时间片后,调度程序将 CPU 资源交给另一个进程。这样在一个多道程序( multi programming )的系统中,用户就感觉系统中的程序好像在并行执行。所以在石器时代的操作系统中,进程是 CPU 调度的基本单位。
随着时间的发展,人们在使用以上的模型编写程序的时候遇到了以下的问题: 1. 一个进程大量的创建执行时间较短的进程,降低了系统的性能。例如早期的服务器程序对于每一个请求,都需要创建一个进程来对其进行处理。 2. 由于不同的进程在不同的地址空间中,进程间除了使用进程间通信( interprocess communication )的方式以外,没有其它办法对数据进行共享。为了解决以上两个问题,人们创造了线程模型。在线程模型中,进程变成了获取系统资源的最小单位,而线程变成了获取 CPU 资源的最小单位。所以当进程被创建的时候,进程必须自己创建一个线程(主线程),已让操作系统的调度程序可以将 CPU 时间片分配给该线程。由于线程是通过进程而不是通过系统获得资源,所以在一个进程中创建一个新的线程的时候,只需要向进程申请线程栈就可以了,而不需要再向系统申请其它资源。这样的设计大大的降低了系统的负担,所以线程也叫做轻量级进程。同时,由于多个线程可以共享同一个进程的地址空间以及其向系统申请的所有资源,所以在同一个进程中的多个线程之间的通信是非常容易的。
我们一般使用线程解决两类程序设计问题,第一类是通过使同一个进程中的线程并行运行,来提高进程的吞吐率和系统 CPU 的使用率。典型的情况是一个服务器程序,理论上它可以对每一个请求创建一个线程来对其进行处理,以提高系统的吞吐率。第二类是可以让进程中的线程异步( asynchronize )运行,以使程序不至于进入假死状态。试想用户点击了某个 GUI 程序中的一个按钮,点击该按钮会让程序向服务器上传一个文件,假设该文件很大,需要 30 分钟才能将文件上传到服务器上。如果我们在设计的时候在 GUI 的线程中上传该文件的话,我们就会发现在上传该文件的同时,我们的 GUI 程序就无法接受任何其它的操作了。这时,程序就进入了假死的状态,我们可怜的用户就只有坐在电脑前傻傻的看着电脑屏幕,而不知道发生了什么事。
下面就让我们来看一下,在 Windows 平台下面,如何来创建以及销毁一个线程。在 Windows 中,我们使用 API 函数 CreateThread