c# 并发编程系列之一:进程、线程、线程池的基本概念
近几年,并发编程的发展非常迅速,而且已经成为现代软件中必不可少的一部分,在这样的背景下,
出现了各种各样处理并发的技术,比如响应式编程、异步编程等;并发编程比起传统编程要稍复杂一些,
好在C#中提供了丰富的类库来支持并发的处理,特别是 c# 5.0 中引入 async/await关键字后,更是在语言层面
给出了更高级的解决方案,也降低了编码的难度,本系列将介绍并发编程的相关知识 。
在了解并发(Concurrency)之前,我们有必要先了解一下进程和线程相关的概念,因为并发编程和这两者息息相关。
一、进程(Process)
进程的概念是60年代初首先由麻省理工学院的 MULTICS 系统和IBM公司的 CTSS/360 系统引入的。
我们知道,早期的操作系统都是单道批处理系统,即一个时间段只有一个程序能加载到内存中运行,除非程序执行完毕,
否则其他程序只能等待,这对CPU资源是极大的浪费;后来多道技术出现了,它允许内存中同时加载多个程序,这些程序在操
作系统的管理下并发执行。这个时候【程序】这个概念已经不足以反映程序并发执行时所具有的特性,所以引入【进程】的概念。
简而言之,引入【进程】的目的就是为了更好的描述程序的并发执行过程。
在现代操作系统中,进程已经是其中的一个核心概念,它是程序的一次执行过程。当我们在桌面双击一个应用程序图标
的时候, 操作系统将程序代码加载到内存中并为程序的执行准备相应的资源和环境,这个时候进程就产生了。从用户角度看,
进程就是正在运行着的应用程序,下图就是笔者电脑上的部分进程。
一个应用程序至少对应着一个进程,程序的运行过程就体现在操作系统对进程的管理和执行上。
二、进程的组成
进程由PCB、程序段、数据段三部分组成。
(1)程序段:程序代码存放在此。
(2)数据段:程序运行时使用、产生的数据。
(3)PCB:PCB(Process Control Block 进程控制块)包含了操作系统对进程进行管理所需的全部信息(包括
进程标志符、CPU状态、寄存器、程序状态字PSW、进程状态、进程优先级、事件、进程上下文等),
它是进程存在的唯一标识,和进程一一对应,操作系统就是通过PCB感知进程的存在并管理进程的。
三、进程的状态
进程有3种基本状态,如下:
1、就绪(Ready)状态:已获取执行所需资源,等待被CPU执行。
2、运行(Running)状态:在CPU中执行。
3、阻塞(Blocked)状态:被中断执行。
这3种状态之间的转化关系如下:
进程在任一时刻只有一种状态,操作系统通过对进程的创建、终止、阻塞、唤醒、挂起、激活、撤销等操作来完成状态间的切换。
四、进程中PCB的组织方式
PCB是按一定的数据结构来记录进程信息的,系统中有多少个进程就对应有多少个PCB,它们之间是一对一关系,
操作系统对进程的管理就体现在对PCB的管理上,多个PCB是按照一定的形式来组织的,如下:
1.线性表方式:不论进程的状态如何,将所有的PCB连续地存放在内存的系统区,这种方式适用于系统中进程数目不多的情况。
2.索引表方式:该方式是线性表方式的改进,系统按照进程的状态分别建立就绪索引表、阻塞索引表、运行索引表等。
3.链接表方式:系统按照进程的状态将进程的PCB组成队列,从而形成就绪队列、阻塞队列、运行队列等。
五、线程(Thread)
(1)为什么引入线程?
我们知道在操作系统中,进程的引入是为了提高了计算机资源的利用效率。但在进一步提高【进程】的并发性时, 人们发现
不同进程之间进行切换的开销越来越大,同时进程间通信的效率也受到限制,所以又引入了【线程】的概念,【线程】是为了实现
如下2个目的:
❐ 简化进程间的通信,
❐ 以更小的开销来提高进程内的并发程度。
(2)线程的定义
线程是进程中的一个运行实体, 是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
一个进程中包含一个或多个线程,他们有各自的内存空间,同时共享进程的内存和资源。
(3)线程的优点
❐ 创建一个新线程花费的时间少,结束也如此。
❐ 两个线程的切换花费时间少。
❐ 线程间通信效率高。
六、线程池(ThreadPool)
虽然线程相比进程其创建的开销已经小很多了,但为了进一步降低这种开销,对进程内的线程进行复用就成了一个自然的选择,这就产生了
另外一种技术:【线程池】 ,线程池是线程的容器,它的原理是这样的:系统在某个时刻为应用程序创建一定数量的线程后放入线程池中备用,
当需要使用的时候,就直接从线程池中取出来,用完后再放回到线程池中,如果要执行的任务数比备用的线程数多,就创建新线程, 此外还可以
设置线程池允许的最大/最小线程数,系统根据线程池的配置来管理线程的生命周期 ,图示如下: