程序:包含作为文件存储在磁盘上的代码或一组处理器指令。当程序中的代码被加载到内存并由处理器执行,就生产出了一个进程。
进程:进程是由一个或多个线程执行的计算机程序的实例。是系统进⾏资源分配的最⼩单位。
线程:由调度程序独立管理的编程指令的最小单元。通常情况下,线程是进程的一个组件。
协程:可以暂停执行的函数。
程序和进程
程序和进程类似于类和对象。多个进程可以指向同一个程序。
用户下达执行程序的命令后,就会产生进程。同一程序可产生多个进程(一对多关系),以允许同时有多位用户执行同一程序,却不会相冲突。
运行程序时,先把程序加载到磁盘中,供CPU读取指令和数据。可执行代码的部分加载到.text区,全局变量和常量这类数据被加载到.data区。除了这两个部分,还需要在内存空间开辟一些空间来存放运行时的数据,比如用户的输入输出等,这部分属于堆栈区。
这个时候,全部的这些区域就构成了进程的内存布局,脱离了程序的概念。
对于像是C或是Rust这类编译语言,编译的结果就是一个程序本身。 对于py或是js这样的解释性语言,想要运行就需要告诉计算机解释器的选择,比如python main.py或是node main.js。这个时候,main.py并不是一个程序,他就是一段计算机不能理解的源代码,加载到内存.text的是python本身,main.py会被当做数据。
进程(process)和线程(thread)
一个场景:公司中有很多项目组,每个项目组有许多员工。每个员工有自己的资源(笔记本),每个员工也可以使用公司的公共资源(服务器)。当公司分配任务给项目组时,所有员工都应该能独立完成各自的任务,并最终合作完成一个完整的项目。
公司:程序 项目组:进程 员工:线程
那么可以把公司看作程序,项目组作为进程(并不恰当),员工就是公司内部能做出贡献的最小单位。员工对项目组就是thread对process的关系。
一个组可以只有一个员工,也可以有多个员工,所以我们有单线程进程和多线程进程: 单线程进程:一个线程执行所有的操作序列。 多线程进程:多个线程的共同结果。 可以说,不存在没有线程的进程(上古时期没有线程的概念,那时的进程可以被看做单个线程)
线程一出生就被分配好资源(注册表、栈内存等),类似于给员工配的笔记本。同时所有的线程都共享进程的代码,或是堆内存,类似于项目组的茶歇。
多线程的意义在于⼀个应⽤程序中,有多个执⾏部分可以同时执⾏。但操作系统并没有将多个线程看做多个独⽴的应⽤,来实现进程的调度和管理以及资源分配。 为了不让线程资源被浪费,就会出现调度器来调度各个线程的执行内容和顺序。这个时候就涉及到并发和并行的内容:
线程的并发和并行
并发(Concurrency)指的是在同一时间段内处理多个任务,但每个任务不会真正地同时执行。 并行(Parallelism)则是指在同一时刻同时执行多个任务。
在单核处理器上,只能实现并发,单核处理器可以通过操作系统的任务切换(上下文切换)实现并发运行。例如,它可以在短时间内不断切换执行不同的任务,使得看起来多个任务在同一时间段都在进行,但实际上每次只有一个任务在被执行;不能实现并行,因为只有一个核心,无法在同一时刻同时执行两个任务。
多核处理器能真正实现并行处理。每个核心可以同时执行不同的任务,因此可以同时处理多个线程或进程。
线程和协程
一个进程中能有多个线程,一个线程中也可以有多个协程。
协程(Coroutine) 可以看成是可以暂停的函数。 作为一种轻量级的并发编程方式,通常用于处理需要等待的操作,比如 I/O 操作、网络请求或动画效果等,它通过让出控制权的方式来实现任务的异步执行,从而避免阻塞主线程。
协程的暂停完全由程序控制,发生在用户态上;而线程是由操作系统内核来进行切换,发生在内核态上。所以协程比线程资源占用少。
Unity中经常会用到协程,是使用IEnumerator
接口实现迭代器来完成的。不同的语言底层实现不一样,但都离不开yield return
。
参考: