在前一章博客(《程序、进程与线程(一)》)中,我们已经了解了程序与进程的概念与区别,今天我们再来讲讲进程与线程。进程用得好好的为何还要出现线程呢?首先我们来看看进程的一些局限性。
一、进程的局限性
1、创建子进程时每次都要把父进程的数据都copy一份,造成资源空间的冗余浪费。
2、子进程和父进程的数据交互比较麻烦。不同的进程位于不同的地址空间,必须通过共享内存或者通信机制。
3、系统在进行进程的调度时还涉及到资源的分配与状态转换等一系列动作,开销大。
二、线程
1、优势
同一进程的不同线程共享代码段、数据和文件——进程间通信较方便,但它们有着自己的线程ID、程序计数器、寄存器和栈。
进程在创建线程时的开销大大减少,不再需要copy数据和文件。
线程是CPU使用的基本单位。
2、进程与线程之间的比较
#同一进程的多线程间调度时不引起进程的切换,不同进程的线程间调度,需要进行进程的切换
线程上下文切换时的虚拟地址空间是相同的,但是进程是不同的。因此进程间的切换需要切换页目录以及使用新的地址空间,而线程不需要。
#并发性
一个进程的多个线程可并发执行(多个进程可并发执行)
#资源的拥有
进程独占资源,而线程不占有资源。也就是线程不拥有系统资源、代码段和数据段。(它只是去使用所处进程所拥有的代码段和数据段)
#内存空间的占用
不同进程各自占有4GB的内存空间,同一进程的不同线程间共同使用4GB的内存空间。(涉及到虚拟内存的知识)
3、特点
线程是进程的一个属性(调度属性),是被系统独立调度的基本单位。
#一个进程可以创建多个线程,这些线程共享进程拥有的全部资源
#多个线程之间并发执行,切换时快速简便
4、单线程与多线程的内存空间构成
三、两种线程
1、用户线程与内核线程
A、用户线程:对用户线程的支持通常位于内核之上,通过一个用户级线程库来实现。线程库提供了对线程的创建、调度和管理的支持,这无需来自内核的支持。
B、内核线程:内核线程由操作系统直接支持,内核在内和空间内实现了线程的创建、调度和管理。
2、用户线程的优缺点
A、优点:
#性能好:低成本的线程操作
#灵活度高:调度是可以应用于特定的调度程序的
#易移植的:用户线程库易于移植
B、缺点:
#如果一个用户级的线程阻塞在内核中,那么这整个进程都会阻塞掉。
#无法使用多处理器进行并行处理。
3、两种线程的结构
A、用户线程(多对一的模式)
也就是内核中创建一个线程,再由这个线程与用户层面上某个进程中的多个线程向对应。这就是所谓的“多对一”模式。这种模式下每个进程只分配到了一个内核线程,一旦该进程中某个用户线程发生阻塞,它会占据该进程所拥有的唯一一个内核线程,自然其他线程也就没法执行,整个进程都会被阻塞。
B、内核线程(一对一的模式)
每个进程的所有线程都与内核中的某个内核线程相对应。因此进程不会因为某个线程的阻塞而导致整个进程都被阻塞。但是内核线程的开辟开销比较大,这种模式下可开启的进程数量更加有限,容易过多地增加操作系统的负担。
C、用户线程+内核线程(多对多的模式)
这种模式下,一个进程中分配了多个内核线程。比如进程A中的用户线程2发生了阻塞,那么只有用户线程2和3会被阻塞,进程A中的用户线程1还是可以继续执行。
优点:开发者能够创建所需的用户线程,而且相应的内核线程能够在多处理机环境中并行运行。而且当一个线程执行导致阻塞的系统调用时,内核能够调度该进程中的其它线程执行。
四、线程的状态转换图
小结:进程是资源分配的基本单位,而线程是CPU调度的基本单位。因此,某种意义上可以说在支持线程的操作系统中没有真正意义上的进程调度,而都是线程调度。线程的出现主要是为了弥补进程各种操作开销大的问题,除这点外,线程和进程是差不多的。多线程和多进程一样,一方面为了充分利用CPU资源,另一方面是为了优化用户交互体验。