go高级进阶:goroutine的创建、休眠与恢复


当前第2页 返回上一页

image.png

goroutine的创建

carbon (14).png 通过go tool compile -S main.go 我们来看看发生了什么?

carbon (15).png 汇编过于太长,只截取其中一部分。
我们看到有一行CALL runtime.newProc()的函数被调用了,这是通过起关键字go func创建goroutine的入口

carbon (16).png 通过gp:=getg()来获取g0,然后通过systemstack切到g0栈,再执行newproc1,newproc1就是我们的goroutine诞生的地方。我们来看看newproc1干了什么:

carbon (18).png

  1. 如果我们的func为nil,则报错
  2. 如果我们的func的参数太多,则报错
  3. 获取本地的p
  4. 尝试从本地的p的gfree上获取一个不用的g,或者从全局的p中获取
  5. 没有获取到空闲的g的时候,则去创建一个g,默认大小为2k
  6. 新创建的g的状态gdead,防止gc错扫面
  7. 将新的g加入全局的allg列表中
  8. 初始化这个g的一些参数
  9. 将我们的func和这个g绑定
  10. 初始化完成后,将这个g的状态设置为runable,处于可以被执行状态
  11. 通过runqput将g放入p的队列,p的队列满的话,就放入全局队列
  12. 尝试通过wakep唤醒一个正处于休眠的p来执行

至此一个新的goroutine创建完毕。

gopark(goroutine的休眠)

goroutine的切换涉及到一个很重要的函数gopark。

carbon (13).png gopark的作用:

  1. 将running状态的goroutine设置为waiting
  2. 解除goroutine和当前工作线程M的关系
  3. 获取一个新goroutine来运行

gopark函数的关键就是mcall函数调用的park_m。

carbon (19).png park_m:

  1. gopark通过mcall将当前线程的堆栈切换到g0的堆栈
  2. 保存当前goroutine的上下文(pc、sp寄存器->g.sched)
  3. 在g0栈上,调用park_m
  4. 将当前的g从running状态设置成waiting状态
  5. 通过dropg来解除m和g的关系
func dropg() {
    _g_ := getg()
    setMNoWB(&_g_.m.curg.m, nil)
    setGNoWB(&_g_.m.curg, nil)
}
复制代码
  1. 最后通过schedule来发起新一轮的调度 schedule()->execute()->gogo(),gogo尝试从gobuf中恢复出协程执行状态并跳转到上一次指令处继续执行。

goready (goroutine的唤醒)

与gopark相反的,有一个goready的函数,它的作用就是唤醒waiting状态的goroutine

carbon (20).png 还是通过systemstack切到g0栈,在g0栈上发起调度

carbon (21).png

  1. 获取goroutine的状态
  2. 将waiting状态的goroutine切换到runable状态
  3. 尝试唤起一个p来执行当前goroutine


本文来自:掘金

感谢作者:假装懂编程

查看原文:go高级进阶:goroutine的创建、休眠与恢复

返回前面的内容

相关阅读 >>

Golang 能做前端吗?

Go map定义的几种方式以及修改技巧

Go timer 是如何被调度的?

Golang令牌桶实现 [Go-rate] 速率限制器

Golang怎么判断指针是否为空

web安全学习方法与职业介绍-1

Golang context 详解

arts #5

Go每日一库 [Go-rate] 速率限制器

聊聊Gost的countwatch

更多相关阅读请进入《Go》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...