当 Goroutines 切换时 CPU context 会发生什么?
What happens with CPU context when Goroutines are switching?
如果我正确理解了 goroutines 如何在系统线程之上工作——它们 运行 一个接一个地从队列中取出。但这是否意味着每个 goroutine loads\unloads 都是 CPU 的上下文?如果是,系统线程和 goroutine 之间有什么区别?
最重要的问题是上下文切换的时间成本。正确吗?
检测哪个goroutine请求了哪些数据的机制是什么?例如:我正在从 goroutine A 向 DB 发送请求并且不等待响应,同时发生切换到下一个 goroutine。系统如何理解请求来自 A 而不是来自 B 或 C?
Goroutines、内存和 OS 线程
Go 有一个分段堆栈,可以根据需要增长。 Go 运行时进行调度,而不是 OS。运行时将 goroutine 多路复用到相对较少的实际 OS 线程上。
Goroutines 切换成本
Goroutine 是协同调度的,当发生切换时,只需要 3 个寄存器 saved/restored - 程序计数器、堆栈指针和 DX。从 OS 的角度来看,Go 程序的行为就像一个事件驱动程序。
协程和CPU
您无法直接控制运行时将创建的线程数。可以通过调用 runtime.GOMAXPROCS(n)
.
设置变量 GOMAXPROCS 来设置程序使用的处理器内核数
程序计数器
完全不同的故事
在计算中,程序是计算机执行的一组特定的有序操作。指令是程序向计算机处理器发出的命令。在计算机中,地址是内存或存储中的特定位置。程序计数器寄存器是处理器使用的一小组数据保存位置之一。
这是一个关于程序如何工作和相互通信的不同故事,它与 goroutine 主题没有直接关系。
来源:
女士,Ps
A "G" 只是一个 goroutine。它由类型 g 表示。当一个 goroutine 退出时,它的 g 对象被 returned 到一个空闲 gs 池中,以后可以被其他 goroutine 重用。
"M" 是一个 OS 线程,可以执行用户 Go 代码、运行时代码、系统调用,或者处于空闲状态。它由类型 m 表示。一次可以有任意数量的 Ms,因为任意数量的线程都可能在系统调用中被阻塞。
最后一个"P"表示执行用户Go代码所需的资源,比如调度器和内存分配器状态。它由类型 p 表示。正好有 GOMAXPROCS Ps。可以将 P 视为 OS 调度程序中的 CPU,而 p 类型的内容类似于 per-CPU 状态。这是放置需要分片以提高效率的状态的好地方,但不需要 per-thread 或 per-goroutine.
调度程序的工作是匹配 G(要执行的代码)、M(在何处执行)和 P(执行它的权利和资源)。当 M 停止执行用户 Go 代码时,例如通过进入系统调用,它 return 将其 P 放入空闲 P 池。为了恢复执行用户 Go 代码,例如在系统调用 return 上,它必须从空闲池中获取 P。
所有 g、m 和 p 对象都是堆分配的,但永远不会释放,因此它们的内存保持类型稳定。因此,运行时可以避免调度程序深处的写障碍。
用户堆栈和系统堆栈
每个 non-dead G 都有一个与之关联的用户堆栈,这是用户 Go 代码执行的地方。用户堆栈从小开始(例如 2K)并动态增长或收缩。
每个 M 都有一个与之关联的系统堆栈(也称为 M 的 "g0" 堆栈,因为它是作为存根 G 实现的),并且在 Unix 平台上,还有一个信号堆栈(也称为 M 的 "gsignal"堆栈)。系统和信号堆栈不能增长,但足够大以执行运行时和 cgo 代码(纯 Go 二进制文件中为 8K;cgo 二进制文件中为 system-allocated)。
运行时代码经常使用 systemstack、mcall 或 asmcgocall 临时切换到系统堆栈,以执行不能被抢占、不能增加用户堆栈或切换用户 goroutine 的任务。系统堆栈上的代码 运行 隐含地 non-preemptible 并且垃圾收集器不扫描系统堆栈。而运行在系统栈上,当前用户栈不用于执行。
参考:https://github.com/golang/go/blob/master/src/runtime/HACKING.md
如果我正确理解了 goroutines 如何在系统线程之上工作——它们 运行 一个接一个地从队列中取出。但这是否意味着每个 goroutine loads\unloads 都是 CPU 的上下文?如果是,系统线程和 goroutine 之间有什么区别?
最重要的问题是上下文切换的时间成本。正确吗?
检测哪个goroutine请求了哪些数据的机制是什么?例如:我正在从 goroutine A 向 DB 发送请求并且不等待响应,同时发生切换到下一个 goroutine。系统如何理解请求来自 A 而不是来自 B 或 C?
Goroutines、内存和 OS 线程
Go 有一个分段堆栈,可以根据需要增长。 Go 运行时进行调度,而不是 OS。运行时将 goroutine 多路复用到相对较少的实际 OS 线程上。
Goroutines 切换成本
Goroutine 是协同调度的,当发生切换时,只需要 3 个寄存器 saved/restored - 程序计数器、堆栈指针和 DX。从 OS 的角度来看,Go 程序的行为就像一个事件驱动程序。
协程和CPU
您无法直接控制运行时将创建的线程数。可以通过调用 runtime.GOMAXPROCS(n)
.
程序计数器
完全不同的故事
在计算中,程序是计算机执行的一组特定的有序操作。指令是程序向计算机处理器发出的命令。在计算机中,地址是内存或存储中的特定位置。程序计数器寄存器是处理器使用的一小组数据保存位置之一。
这是一个关于程序如何工作和相互通信的不同故事,它与 goroutine 主题没有直接关系。
来源:
女士,Ps
A "G" 只是一个 goroutine。它由类型 g 表示。当一个 goroutine 退出时,它的 g 对象被 returned 到一个空闲 gs 池中,以后可以被其他 goroutine 重用。
"M" 是一个 OS 线程,可以执行用户 Go 代码、运行时代码、系统调用,或者处于空闲状态。它由类型 m 表示。一次可以有任意数量的 Ms,因为任意数量的线程都可能在系统调用中被阻塞。
最后一个"P"表示执行用户Go代码所需的资源,比如调度器和内存分配器状态。它由类型 p 表示。正好有 GOMAXPROCS Ps。可以将 P 视为 OS 调度程序中的 CPU,而 p 类型的内容类似于 per-CPU 状态。这是放置需要分片以提高效率的状态的好地方,但不需要 per-thread 或 per-goroutine.
调度程序的工作是匹配 G(要执行的代码)、M(在何处执行)和 P(执行它的权利和资源)。当 M 停止执行用户 Go 代码时,例如通过进入系统调用,它 return 将其 P 放入空闲 P 池。为了恢复执行用户 Go 代码,例如在系统调用 return 上,它必须从空闲池中获取 P。
所有 g、m 和 p 对象都是堆分配的,但永远不会释放,因此它们的内存保持类型稳定。因此,运行时可以避免调度程序深处的写障碍。
用户堆栈和系统堆栈
每个 non-dead G 都有一个与之关联的用户堆栈,这是用户 Go 代码执行的地方。用户堆栈从小开始(例如 2K)并动态增长或收缩。
每个 M 都有一个与之关联的系统堆栈(也称为 M 的 "g0" 堆栈,因为它是作为存根 G 实现的),并且在 Unix 平台上,还有一个信号堆栈(也称为 M 的 "gsignal"堆栈)。系统和信号堆栈不能增长,但足够大以执行运行时和 cgo 代码(纯 Go 二进制文件中为 8K;cgo 二进制文件中为 system-allocated)。
运行时代码经常使用 systemstack、mcall 或 asmcgocall 临时切换到系统堆栈,以执行不能被抢占、不能增加用户堆栈或切换用户 goroutine 的任务。系统堆栈上的代码 运行 隐含地 non-preemptible 并且垃圾收集器不扫描系统堆栈。而运行在系统栈上,当前用户栈不用于执行。