GO语言CSP并发模型及GMP调度模型

一、CSP模型

CSP(Communicating Sequential Processes)是一种形式化的并发模型,最初由 Tony Hoare 于1978年提出,为了描述并分析并发系统中进程之间的交互。Go 语言的并发模型大量借鉴了 CSP 的概念,并通过 goroutines 和 channels 实现了它。

在 Go 语言的 CSP 并发模型中,核心概念包括:

Goroutines

  • Goroutine: 是 Go 中实现并发的基本单位,类似于轻量级线程。每个 Goroutine 在独立的执行路径上运行函数,但与操作系统线程不同,Goroutines 是由 Go 运行时管理的,并不是直接映射到内核线程上。
  • 调度器: Go 运行时包含一个内部的调度器,它负责把 Goroutines 分配给可用的逻辑处理器(P),然后绑定到操作系统线程(M)上执行。

Channels

  • Channel: 是 Goroutines 之间进行通信和同步的主要方式。它是一个管道,可以传递指定类型的数据。Channel 可以是缓冲的或非缓冲的,区别在于是否允许发送操作在没有对应的接收操作时仍然完成。
  • 阻塞与同步: 默认情况下,发送操作和接收操作都是阻塞的。如果 Channel 空,接收者会阻塞等待;如果 Channel 满(对于非缓冲 Channel,即容量为零),发送者会阻塞等待。
  • Select 语句: Go 提供了 select 语句,允许 Goroutines 同时等待多个 Channel 操作,并根据哪个 Channel 先准备好来选择相应的操作。

CSP 并发模型的要点:

  1. 并发组件作为独立单元运行:在 Go 中,这些并发组件是 Goroutines,每个 Goroutine 都有自己的执行路径。

  2. 消息传递而非共享内存:CSP 强调在并发单元之间通过消息传递进行通信,而不是共享内存。Go 中的 Channel 实现了这一点,强制进行数据的复制而不是共享。

  3. 同步通信机制:在 CSP 中,进程间的通信往往是同步的,意味着当一个进程发送消息时,它会阻塞直到另一个进程准备好接收消息。Go 的 Channel 同样遵循这一模式,尽管也支持缓冲 Channel 来允许一定程度的异步通信。

  4. 避免锁和竞态条件:因为 CSP 倾向于使用消息传递而非共享内存,它本质上避开了需要使用互斥锁来控制并发访问共享资源的需求。

在 Go 中,CSP 并发模型被认为是比较简洁和有效的,因为它让开发者能以顺序的思维来编写并发程序,同时可大幅减少死锁、竞态条件等并发问题的出现。此外,通过 Goroutines 和 Channels,Go 程序员可以构建复杂的并发结构,如管道、工作池以及其他协调并发任务的模式。

二、GMP模型

GMP 是 Go 语言运行时的调度模型,这个模型涉及三个主要的实体:Goroutine(G)、Machine(M)和Processor(P)。

Goroutine (G)

  • Goroutine 是 Go 语言中的协程,是执行用户代码的实体。Goroutines 比线程更轻量级,可以快速创建和销毁。它们并不直接对应到操作系统线程,而是由 Go 运行时管理和调度。

Machine (M)

  • Machine 表示真实的操作系统线程。Go 运行时会在需要执行 Goroutines 的时候创建 M。每个 M 都会关联一个 P 来获取 Goroutines 列表进行执行。

Processor §

  • Processor 是逻辑处理器,代表了执行 Go 代码所需的资源。P 维护着一个本地的队列,用来存放等待运行的 Goroutines。每个 P 最多只能绑定到一个 M。P 的数量通常由 GOMAXPROCS 环境变量控制,也可以在程序运行时通过 runtime.GOMAXPROCS() 函数设置。

调度过程

  1. 启动:当 Go 程序开始运行时,它会根据 GOMAXPROCS 的值初始化相应数量的 P,并启动 M 来执行程序中的 Goroutines。

  2. 执行:每个 M 都会尝试从与之绑定的 P 的本地队列中取出 Goroutine 来执行。如果 P 的本地队列为空,M 可以尝试从其他 P 的本地队列或者全局队列中偷取(work stealing)Goroutines。

  3. 阻塞和唤醒:如果一个 Goroutine 因为某些原因(如等待 I/O)而阻塞,执行该 Goroutine 的 M 会将其置于等待状态,并尝试从 P 的队列中取出另一个 Goroutine 来执行。被阻塞的 Goroutine 在资源准备好后会被唤醒并放入某个 P 的本地队列中。

  4. 系统调用和网络轮询器:Go 运行时拥有一个网络轮询器(netpoller),它负责处理可能阻塞的系统调用,如网络 I/O。当 Goroutine 执行系统调用时,M 可以分离(detach)P 并附加到其他 Goroutines 上;一旦系统调用完成,轮询器会重新唤醒 G,并找一个空闲的 P 给它继续执行。

  5. 调度策略:Go 调度器使用了非抢占式的调度策略,意味着除非 Goroutine 显式地放弃控制(如通过调用 runtime.Gosched()、发生阻塞的系统调用、channel 操作等),否则它们会一直运行,不会被强行抢占。

GMP 模型允许高效地管理大量的 Goroutines,提供了可扩展性和弹性,并最小化了操作系统上下文切换的开销。这个模型的设计使得 Go 能够在并发编程中保持简单和高效。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值