进程、线程与协程 | 青训营笔记

117 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 17 天

课程内容与选题缘由

昨天在整理并发编程的课程笔记时,发现自己对协程的概念有些模糊,因此系统地了解了一下进程、线程、协程的区别,并做一份汇总。

进程、线程与协程的区别

进程是计算机中的一个基本概念,表示正在运行的一个程序实例。一个进程包含了程序的代码、数据、堆栈、文件描述符等信息,它是计算机中进行资源分配调度的基本单位。每个进程都拥有独立的地址空间,相互之间不能直接访问,进程之间的通信需要使用IPC(进程间通信)机制,如管道、消息队列、共享内存等。

线程是进程中的一个执行单元,是进程中的实体。每个进程中可以有多个线程,它们共享进程的内存空间和其他资源。线程之间可以直接访问同一进程中的变量和数据结构,因此线程之间的通信比进程之间的通信更为方便和高效。线程在操作系统层面上也是一个基本的调度单位,由操作系统负责调度和分配CPU资源,使得不同的线程可以并发执行。

协程是一种用户态的轻量级线程。协程和传统的线程不同,它是在用户内存空间中实现的,而不是由操作系统进行调度,因此可以在一个线程中同时执行多个协程。协程在并发编程中非常有用,因为它们可以避免线程切换带来的开销,并且可以有效地利用CPU资源。在Go语言中,协程被称为Goroutine,它可以非常方便地实现并发编程。

这三种程序执行方式各有优缺点,使用场景也不同。进程适合处理独立的任务,可以在不同的进程之间分配和管理资源;线程适合处理复杂的计算和通信任务,可以充分利用多核CPU资源;协程适合处理I/O密集型任务,可以避免线程切换带来的开销,并且可以高效地利用CPU缓存。

进程、线程与协程的具体例子

假设你正在使用一个calculator软件,以下是进程、线程和协程在该软件中的典型角色:

  • 进程:在你启动calculator软件时,操作系统会创建一个新的进程来运行该软件。这个进程包含了calculator软件的代码、数据、堆栈等信息,是操作系统中的一个独立实体。这个进程可以访问操作系统提供的各种资源,如内存、文件、网络等。
  • 线程:在calculator软件中,可能会有多个线程用于不同的任务,例如GUI线程、计算线程、IO线程等。GUI线程用于渲染用户界面,处理用户的输入和输出;计算线程用于执行计算任务,如加减乘除等;IO线程用于处理与外部设备或网络的交互。这些线程共享进程的内存空间和其他资源,可以相互协作完成各自的任务。
  • 协程:在calculator软件中,可能会使用协程来实现某些特定的功能,例如异步处理和非阻塞IO等。异步处理可以避免GUI线程的阻塞,提高用户体验;非阻塞IO可以避免IO线程的阻塞,提高程序的性能。协程可以在单个线程内部进行切换,避免了线程切换带来的开销,提高程序的效率。

综上所述,进程、线程、协程都在calculator软件中分别负责不同的任务,并协同工作以实现软件的功能。

为什么Gouroutine被称为轻量级线程?

相比较于传统的操作系统线程,Goroutine 确实可以被看作是一种轻量级线程。这是由于以下几个方面的原因:

  1. Goroutine 的创建和销毁开销很小。相比较于操作系统线程,Goroutine 创建和销毁的开销非常小,只需要几百个字节的栈空间,这使得可以轻松创建数以万计的 Goroutine。
  2. Goroutine 的切换开销很小。相比较于操作系统线程,Goroutine 的切换开销也非常小,因为 Goroutine 之间的切换不需要进行上下文切换和内核态和用户态之间的切换,而是通过 Go语言运行时调度器在用户态下进行切换,这使得 Goroutine 切换的开销大大降低。
  3. Goroutine 的栈空间是动态的。相比较于操作系统线程,Goroutine 的栈空间是动态分配的,栈的大小会根据需要进行自动调整。这使得可以在同一个进程中创建更多的 Goroutine,从而提高了并发性能。
  4. Goroutine 的同步开销较小。相比较于使用共享内存的线程同步机制,Goroutine 通常使用 Channel 进行同步,这种基于消息传递的方式可以避免共享内存所带来的同步开销和死锁等问题。

综上所述,Goroutine 是一种轻量级的并发执行机制,可以高效地实现大规模并发,提高程序的效率。它的轻量级设计使得可以轻松创建数以万计的 Goroutine,而不会受到操作系统线程限制。同时,Goroutine 的调度和切换机制也使得其切换开销较小,提高了并发性能