深入理解 go协程 调度机制

Thread VS Groutine

这里主要介绍一下Go的并发协程相比于传统的线程 的不同点:

创建时默认的stack大小

  • JDK5 以后Java thread stack默认大小为1M
  • C++ 的thread stack 默认大小为8M
  • Grountine 的 Stack初始化大小为2K

所以Grountine 大批量创建的时候速度会更快

和 KSE(Kernel Schedule Entity即内核线程)的对应关系

  • Java Thread是1:1

  • Groutine 是M:N,多对多,如下图。

  • 内核线程是由CPU直接调度,如果一个用户线程对应一个内核线程,调度效率来看肯定是快于多个用户线程对应一个内核线程的。

  • 然而,实际的开发环境中一个用户线程对应一个内核线程 在 高并发场景下出现的频繁内核线程上下文切换(保留线程上下文,更新CPU内部各种寄存器)对系统性能的影响占主要部分。

  • 而Go语言内部实现的线程调度器提供了多个用户线程和一个内核线程对应,这样在高并发场景能够有效降低线程间切换带来的性能消耗。

  • 当然,如果如果仅仅只有几个或者十几个(小于CPU核数)用户线程的应用可能就体现不出Grountine的优势了。

Groutine 调度原理

  • M -- System Thread 「系统线程」
  • P -- Processor 「Go 语言的协程处理器」
  • G -- Goroutine 「协程」

Processor 在不同的系统线程里,每个 Processor 都挂着一个准备运行的协程队列 G-G-G...... 有一个协程正在运行,协程队列依次运行。

Go 启动的时候,会有一个守护线程 G0,计数,会记录每个 Processor 运行完成的协程的数量,如果发现某一个 Processor 在一段时间内没有发生变化(阻塞),就会往这个协程的任务栈里面插入一个特殊的标记,当协程运行遇到非内联函数,就会读到这个标记,将自己中断下来,插到等待协程队列的队尾,切换到其他队列的队尾。

当某一个协程被系统通断了,比如 IO 需要等待的时候,Processor 会把自己加入到其他可使用的系统线程之中,继续执行其他的协程 Goroutine。当被中断的协程被唤醒,完成之后,会把自己加入到其他某一个 Processor 等待队列中,或全局等待队列当中。

当协程被中断的时候,它在寄存器中的运行状态,也会保存在协程对象中;当重新开始运行的时候,就会把运行状态写回寄存器。

举个小例子

go 复制代码
import (
  "fmt"
  "testing"
  "time"
)

func TestGroutine(t *testing.T) {
  for i := 0; i < 10; i ++ {
    // 方法一: 正确
    go func(i int) { // 启动 一个 go routine
      fmt.Println(i)
    }(i)

    // 方法二:错误
    // 如下代码是有问题的
    // i 地址是被所有协程共享的,这个时候打印的结果
    // 会受到其他协程的影响
    // 想要保证代码的正确性,即每一个go routine打印
    // 各自的i 值,需要利用如上启动go routine的代码,
    // 进行值传递,从而让每个goroutine 独享各自的i的地址。
    // go func() {
    //  fmt.Println(i)
    // }()
  }

  time.Sleep(time.Millisecond*50)
}
相关推荐
y = xⁿ3 分钟前
【Leet Code 】滑动窗口
java·算法·leetcode
day day day ...15 分钟前
MyBatis条件误写引发的查询条件污染分析与防范
java·服务器·tomcat
dr_yingli20 分钟前
fMRI(3-1)报告(个体化报告)生成器说明
开发语言·matlab
hrhcode20 分钟前
【java工程师快速上手go】一.Go语言基础
java·开发语言·golang
2601_9507039435 分钟前
Spring IoC入门实战:XML与注解双解
java
带刺的坐椅35 分钟前
Snack JSONPath 项目架构分析
java·json·java8·jsonpath
飞Link43 分钟前
【AI大模型实战】万字长文肝透大语言模型(LLM):从底层原理解析到企业级Python项目落地
开发语言·人工智能·python·语言模型·自然语言处理
妙蛙种子31144 分钟前
【Java设计模式 | 创建者模式】 原型模式
java·开发语言·后端·设计模式·原型模式
LlNingyu1 小时前
Go 实现无锁环形队列:面向多生产者多消费者的高性能 MPMC 设计
开发语言·golang·队列·mpmc·数据通道
Lyyaoo.1 小时前
【JAVA基础面经】线程的状态
java·开发语言