Dmi*_*kov 0 multithreading go coroutine goroutine
如果我正确理解goroutine在系统线程之上的工作方式-它们从队列中一个接一个地运行。但这是否意味着每个goroutine都会将其上下文加载/卸载到CPU?如果是,系统线程和goroutines之间有什么区别?
最重要的问题是上下文切换的时间成本。这是对的吗?
检测哪种goroutine请求哪些数据的基础是什么?例如:我正在从goroutine A向DB发送请求,并且不等待响应,并且同时切换到下一个goroutine。系统如何理解请求来自A而不是来自B或C?
Go具有可根据需要增长的分段堆栈。运行时执行调度,而不是操作系统。运行时将goroutine复用到相对较少的实际OS线程上。
Goroutine是协同调度的,发生切换时,仅需要保存/恢复3个寄存器-程序计数器,堆栈指针和DX。从操作系统的角度来看,Go程序的行为就像一个事件驱动程序。
您不能直接控制运行时将创建的线程数。可以通过调用来设置变量GOMAXPROCS来设置程序使用的处理器内核数量runtime.GOMAXPROCS(n)。
和一个完全不同的故事
在计算中,程序是计算机要执行的一组特定的有序操作。指令是程序赋予计算机处理器的命令。在计算机内,地址是内存或存储中的特定位置。程序计数器寄存器是处理器使用的一小部分数据保存位置之一。
这是关于程序如何工作以及如何相互通信的另一个故事,它与goroutine主题没有直接关系。
资料来源:
小智 5
“G”只是一个 goroutine。它由类型 g 表示。当一个 goroutine 退出时,它的 g 对象会返回到一个空闲 g 池中,并且稍后可以被其他 goroutine 重用。
“M”是一个操作系统线程,可以执行用户 Go 代码、运行时代码、系统调用或处于空闲状态。它由类型 m 表示。一次可以有任意数量的 Ms,因为系统调用中可能会阻塞任意数量的线程。
最后,“P”代表执行用户 Go 代码所需的资源,例如调度程序和内存分配器状态。它由类型 p 表示。正好有 GOMAXPROCS Ps。AP 可以被认为是操作系统调度程序中的 CPU,p 类型的内容就像每个 CPU 的状态。这是放置需要分片以提高效率的状态的好地方,但不需要是每个线程或每个 goroutine。
调度程序的工作是匹配 G(要执行的代码)、M(在哪里执行)和 P(执行它的权限和资源)。当 M 停止执行用户 Go 代码时,例如通过输入系统调用,它将其 P 返回到空闲 P 池。为了恢复执行用户 Go 代码,例如从系统调用返回时,它必须从空闲池获取 P。
所有 g、m 和 p 对象都是堆分配的,但永远不会释放,因此它们的内存保持类型稳定。因此,运行时可以避免调度程序深处的写障碍。
每个非死 G 都有一个与之关联的用户堆栈,这是用户 Go 代码执行的地方。用户堆栈开始时很小(例如 2K),然后动态增长或收缩。
每个 M 都有一个与其关联的系统堆栈(也称为 M 的“g0”堆栈,因为它是作为存根 G 实现的),并且在 Unix 平台上还有一个信号堆栈(也称为 M 的“gsignal”堆栈)。系统和信号堆栈无法增长,但足够大以执行运行时和 cgo 代码(纯 Go 二进制文件中为 8K;cgo 二进制文件中系统分配)。
运行时代码通常使用 systemstack、mcall 或 asmcgocall 临时切换到系统堆栈来执行不得抢占、不得增加用户堆栈或切换用户 goroutine 的任务。在系统堆栈上运行的代码是隐式不可抢占的,并且垃圾收集器不会扫描系统堆栈。在系统堆栈上运行时,当前用户堆栈不用于执行。