“大树根深,才能迎风而立。”
进程:计算机中正在执行的程序的实例,它是操作系统进行资源分配的基本单位。
通过写特殊代码,把多个 CPU 核心都能利用起来,这样的代码就称为“并发编程”。
虽然多进程能够解决问题,但是随着对于效率的要求越来越高,就希望有更好的方式来实现并发编程。
多进程编程最大的问题就是,进程太“重”了。
在创建进程和销毁进程的时候,开销是比较大的(在时间和空间上)。一旦需求场景,需要频繁的创建销毁进程,开销就会非常明显了!
最典型的就是服务器开发了,服务器需要针对每个发送请求的客户端,都单独创建一个进程,由这个进程负责给客户端提供服务。
为了解决进程开销比较大的问题,发明了 “线程”!
线程:是操作系统能够进行运算调度的最小单位,它是进程内的一条执行路径。
对于线程呢?可以理解成,更轻量的进程。也能解决并发编程的问题,但是创建/销毁的开销,要比进程更低。因此,多线程的编程,就成了当下主流的并发编程方式了。
所谓的进程,在系统中,是通过 PCB 这样的结构体来描述,通过链表的形式来组织。
那么系统中,同样也是通过 PCB 来描述线程的(Linux)。
一个进程,其实是一组 PCB。
一个线程,其实是一个 PCB。
两者之间存在了包含关系,一个进程中包含了一个或多个线程,不能没有。
此时,每个线程,都可以独立的到 CPU 上调度执行。
线程是系统“调度执行”的基本单位。
进程是系统“资源分配”的基本单位。
一个可执行程序运行的时候,操作系统就会创建进程,给这个程序分配各种资源(CPU,内存,硬盘,网络带宽……),同时也会在这个进程中,创建出一个或者多个线程,这些线程再去 CPU 上调度执行。
同一个进程中的这些线程,共用同一份系统资源的。
线程比进程更轻量,主要就在于创建线程,省去了“分配资源”的过程,销毁线程也省去了“释放资源”过程。
一旦创建进程,同时也会创建第一个线程,就会给其分配资源,一旦后续创建第二个第三个线程,就不必重新分配资源了。
下面就以一些图和例子来讲解说明:
假设一个进程执行一个任务,执行吃北京烤鸭的任务:
滑稽老铁执行吃一百只北京烤鸭的任务,一百只烤鸭对于滑稽老铁来说,是可以完成的,但就是速度会很慢!
为了提高“吃鸭子”的效率,就引进了“多进程”的方案。
虽然消耗的资源多了,两个房子,两张桌子,两个滑稽老铁。 但是每个房间里的北京烤鸭就只有50份了。两个人吃起来的效率,就比一个快多了。为了创建一个进程,给它分配了房间和桌子等这样的资源。
下面我们引入多线程来看看:
房子和桌子都没有额外的申请,房间和桌子都是同一个,没有再次申请系统资源。这样的吃鸡效率和多进程的方案一样,但是和多进程的方案相比,多线程对于系统的开销较小!
那么如果进一步增加线程的数量呢?
此时每个滑稽老铁负责的北京烤鸭更少了,速度比刚才又进一步提升了!随着线程的数量增加,每个线程需要负责的工作量就会减少,这些线程一旦同时开始工作,那么总的消耗的时间也会进一步减少!
下面进一步增加线程的数量呢?我们一起来看一看:
线程的数量越来越多之后,此时,效率也没办法进一步得到提升了,桌子的空间是有限的,房子的空间也是有限的。当滑稽老铁的数目到达一定数量之后,有些人就够不了桌子了,此时就吃不到北京烤鸭了!
此时就很容易发生“冲突”,由于多个线程使用的是该进程的资源。 一旦发生冲突,就可能使程序出现问题“线程安全问题”,我在之前的博客中有写到。
一旦某个线程抛出异常,这个时候如果能够妥善处理,那还好。一旦处理不当,就可能导致整个进程都崩溃,因此其他线程就会随之崩溃!
难办那就别办了!
进程和线程的区别:
- 进程包含线程(一个进程里可以只有一个线程,也可以有多个线程,但是不能没有线程)
- 进程是系统资源分配的基本单位,线程是系统调度执行的基本单位
- 同一个进程中的线程之间,共用同一份系统资源(内存,硬盘,网络带宽等……)
- 线程是当下实现并发编程的主流方式,通过多线程可以充分利用好,多核CPU(但是也不是线程的数量越多越好,当线程数量到达一定程度之后,就会把多个核心都充分利用完了。此时再继续增加线程数量,可能就会带来一些不好的影响)
- 多个线程之间,可能会相互影响,线程安全问题,一旦线程抛出异常,也可能把其他线程也一起带走。
- 多个进程之间,一般不会相互影响,一旦进程崩溃,不会影响到其他进程
本期的内容,到此结束咯!