• 周五. 3月 1st, 2024

    进程、线程、协程、并发、并行

    root

    6月 8, 2022 #线程, #进程

    一、 进程

    对操作系统来说,进程是资源分配的最小单位,程序启动时,操作系统就会给这个程序分配一块内存空间,对程序本身而言它认为这是一整块连续的内存空间,称为虚拟内存空间,而实际上落实到操作系统内核时通常是一块块的内存碎片。一个进程大小可能是几个G, 进程之间切换开销较大,进程可以实现操作系统的并发。

    这片虚拟内存空间,可以划分为内核空间和用户空间,它们相互隔离,程序即使崩溃了,内核空间也不会受到影响。进程运行在内核空间时称为内核态, 运行在用户空间称为用户太。 内核空间用于执行内核代码,用户空间只用于执行用户程序,若要执行各种IO操作,就需要通过系统调用等方式从用户态切换到内核态进入内核空间进行操作。

    多进程并发有两个缺点: 一是内核的管理成本高,二是无法简单通过内存同步数据,进程间通信较困难。因此,多线程模式出来了。

    二、线程

    对操作系统来说,线程是资源调度的最小单位,线程是进程的一个执行单元,一个进程至少需要包含一个线程(可以包含多个),只有拥有了线程的进程才会被CPU执行。一个线程大小约是几M,线程可以实现进程内部的并发。总结起来就是:进程主要面向内存的分配管理,线程主要面向CPU的调度。

    一个进程下的多个线程是共享这个进程的内存空间的,即线程没有自己独立的内存空间。正因如此,所以一个线程可以读、写甚至清除另一个线程的堆栈。相当于线程之间是没有保护的。但每个线程都有自己的堆栈、程序计数器、寄存器等信息,这些都不是共享的。线程也被称为轻量级进程,与进程调度类似,CPU在线程之间快速切换,就有了线程并行运行的假象。线程间的切换开销要比进程间切换小的多,因为不需要切换页表,虚拟地址空间等等一些东西。

    共享地址空间可以方便的共享对象,但也有一个问题,就是任何一个线程崩溃,进程中所有线程会一些崩溃。

    同时,多线程虽然进一步提高了并发,但在当今互联网高并发场景下,为每个任务都创建一个线程甚至是创建上万个线程来工作是不现实的,因为会消耗大量的内存,而且多线程开发要考虑很多同步竞争等问题,如锁、竞争冲突等。此外,不管是进程还是线程,它们的切换都是由内核控制的,所以线程的切换涉及到用户空间和内核空间的切换(特权模式的切换),然后需要操作系统的调度模块完成线程调度。

    三、协程

    高并发的情况如何更好的提高CPU的利用率呢?于是协程就出现了。如一个进程可包含多个线程,一个线程也可以包含多个协程。一个协程的大小约是几KB。

    线程可以分为 “内核态 “线程和” 用户态 “线程。一个 “用户态线程” 必须要绑定一个 “内核态线程”,但是 CPU 并不知道有 “用户态线程” 的存在,它只知道它运行的是一个 “内核态线程”。

    然后再细化分类一下,内核线程还叫 “线程 (thread)”,用户线程叫 “协程 (co-routine)”。

    协程它不像线程和进程那样需要进行系统内核上的上下文切换(协程切换不涉及特权模式的切换),协程切换只涉及基本的CPU上下文切换,完全在用户空间完成,做的事要比进程线程少,因此切换开销要更小。

    协程的优点:一是可以提高CPU利用率,避免系统内核级的线程间频繁切换造成的资源浪费;二是可以节约内存,一个进程几G、一个线程几M、一个协程几KB;三是稳定性好一些,线程可以通过内存共享数据,但是一个线程挂了,进程中所有线程会一起崩溃。

    协程缺点:协程本质是个单线程,它不能同时使用单个 CPU 的多个核;一旦协程出现阻塞,将会阻塞整个线程。

    四、 并发和并行

    并发:宏观上的“并行”,看起来两个程序A和B在同时运行,但是微观来看其实两个程序的指令是交织着运行的,即运行一会A在运行一会B,通过切换时间片往复交替。(并发就是你有处理多个任务的能力,但不一定同时)
    并行:严格意义的同时执行。例如A和B两个程序分别运行在两个不同的cpu核上,它们互不影响。(并行就是你有同时处理多个任务的能力)

    可以结合下面图片来理解并发和并行:

    root