常见并发模型梳理
@ hanamichi · Sunday, Feb 28, 2021 · 5 分钟阅读 · 更新于 2月 28, 2021

进程线程模型,golang GMP,CSP, actor 模式

并发(concurrency)和并行(parallellism)

并发(concurrency):两个或两个以上的任务在一段时间内被执行。我们不必care这些任务在某一个时间点是否是同时执行,可能同时执行,也可能不是,我们只关心在一段时间内,哪怕是很短的时间(一秒或者两秒)是否执行解决了两个或两个以上任务。

并行(parallellism):两个或两个以上的任务在同一时刻被同时执行。

并发说的是逻辑上的概念,而并行,强调的是物理运行状态。并发“包含”并行。

参考: Rob Pike-golang 并发模式

线程模型

进程是操作系统分配资源的基本单位

线程是操作系统cpu调度的基本单位

在同一进程中,线程的切换不会引起进程切换。在不同进程中进行线程切换,如从一个进程内的线程切换到另一个进程中的线程时,会引起进程切换

操作系统根据资源访问权限的不同,体系架构可分为用户空间和内核空间;

内核空间主要操作访问CPU资源、I/O资源、内存资源等硬件资源,为上层应用程序提供最基本的基础资源

用户空间是上层应用程序的固定活动空间,用户空间不可以直接访问资源,必须通过“系统调用”、“库函数”或“Shell脚本”来调用内核空间提供的资源。

用户线程

用户线程

优点:

  • 可以在不支持线程的操作系统中实现。

  • 创建和销毁线程、线程切换代价等线程管理的代价比内核线程少得多, 因为保存线程状态的过程和调用程序都只是本地过程

  • 允许每个进程定制自己的调度算法,线程管理比较灵活

缺点:

  • 线程发生I/O或页面故障引起的阻塞时,如果调用阻塞系统调用则内核由于不知道有多线程的存在,而会阻塞整个进程从而阻塞所有线程, 因此同一进程中只能同时有一个线程在运行

  • 一个单独的进程内部,没有时钟中断,所以不可能用轮转调度的方式调度线程

  • 资源调度按照进程进行,多个处理机下,同一个进程中的线程只能在同一个处理机下分时复用

内核线程

用户线程

优点:

  • 多处理器系统中,内核能够并行执行同一进程内的多个线程

  • 如果进程中的一个线程被阻塞,能够切换同一进程内的其他线程继续执行(用户级线程的一个缺点)

  • 所有能够阻塞线程的调用都以系统调用的形式实现,代价可观

  • 当一个线程阻塞时,内核根据选择可以运行另一个进程的线程,而用户空间实现的线程中,运行时系统始终运行自己进程中的线程

两级线程模型

用户线程

这种模型是介于用户级线程模型和内核级线程模型之间的一种线程模型。这种模型的实现非常复杂,和内核级线程模型类似,一个进程中可以对应多个内核级线程,但是进程中的线程不和内核线程一一对应;这种线程模型会先创建多个内核级线程,然后用自身的用户级线程去对应创建的多个内核级线程,自身的用户级线程需要本身程序去调度,内核级的线程交给操作系统内核去调度。

Go CSP 模型

Go实现了两种并发形式。

  • 多线程共享内存。其实就是Java或者C++等语言中的多线程开发。常见访问共享变量,线程安全的数据结构等
  • CSP(communicating sequential processes)并发模型。
    • goroutine 是Go语言中并发的执行单位。有点抽象,其实就是和传统概念上的”线程“类似,可以理解为”线程“。
    • channel是Go语言中各个并发结构体(goroutine)之前的通信机制。 通俗的讲,就是各个goroutine之间通信的”管道“,有点类似于Linux中的管道。

Go语言的线程模型就是一种特殊的两级线程模型。

GMP模型

M指的是Machine,一个M直接关联了一个内核线程。 P指的是”processor”,代表了M所需的上下文环境,也是处理用户级代码逻辑的处理器。 G指的是Goroutine,其实本质上也是一种轻量级的线程。

go-gmp

一个M会对应一个内核线程,一个M也会连接一个上下文P,一个上下文P相当于一个“处理器”,一个上下文连接一个或者多个Goroutine。P(Processor)的数量是在启动时被设置为环境变量GOMAXPROCS的值,或者通过运行时调用函数runtime.GOMAXPROCS()进行设置。Processor数量固定意味着任意时刻只有固定数量的线程在运行go代码。Goroutine中就是我们要执行并发的代码。图中P正在执行的Goroutine为蓝色的;处于待执行状态的Goroutine为灰色的,灰色的Goroutine形成了一个队列runqueues

go-gmp

上下文 P 的存在意义是当 m 上的 goroutine 进行系统调用被阻塞时,可以将 P 交由其他的 m 继续进行 goroutine 的执行,即不阻塞业务代码

Actor 模式

Actor模式是一种并发模型,与另一种模型共享内存完全相反,Actor模型share nothing。

所有的线程(或进程)通过消息传递的方式进行合作,这些线程(或进程)称为Actor。

共享内存更适合单机多核的并发编程,而且共享带来的问题很多,编程也困难。随着多核时代和分布式系统的到来,共享模型已经不太适合并发编程

MapReduce就是一种典型的Actor模式

Go的 channel 和 goroutine 跟 actor 模式的理念很类似

关于我

关于我

Hanamichi 的 ❤️ 博客

记录一些 🌈 生活上,技术上的事

全平台玩家(pc-steam, ps4, switch)

职业是后端工程师

正在努力学习

对云原生微服务感兴趣

主要的技术栈是:

  • golang
  • kubernetes
  • 微服务架构

略懂一二:

  • python
  • C/C++
  • java

学习中:

  • Rust
  • JavaScript

目前在 99cloud 工作

– 2021 年 2 月 1 日更新

2020年的计划

2021年的计划

现在回头看看,因为疫情和自身自律性原因,2020年的计划大多都没有实现

  • github issue和wiki记录平时的一些想法,坚持整理然后写入博客

  • 继续阅读 程序员修炼之道代码整洁之道

  • 关注云原声CNCF开源社区动态,争取做些贡献(平时还是太懒,自律性不强)

  • 以 java spring 生态继续研究架构的世界

其他

其他

如果你喜欢我的文章 or 我的项目,或者它们可以给你带来帮助。

You can buy me a coffee. ~

我的微信赞赏码

wechat

社交链接