2024年Go最新golang协程goroutine简介_golang goroutine(5),2024年最新8年Golang开发教你如何写简历

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

在同一时刻,一个线程上只能跑一个goroutine。当goroutine发生阻塞时,runtime会把当前goroutine调度走,让其他goroutine来执行,不让一个线程闲着。

调度策略

go程序由两层构成:program(用户程序)和runtime(运行时)。Runtime 维护所有的goroutines,并通过 scheduler 来进行调度。
goProgramAndRuntime

可运行队列

所有可执行go routine都放在队列中:

  • 全局队列(GRQ):存储全局可运行的goroutine(从系统调用中恢复的G);
  • 本地可运行队列(LRQ):存储本地(分配到P的)可运行的goroutine;
    grqAndLrq
    Go scheduler采用队列轮转法: P周期性地将G调度到M中执行(一小段时间),完成后保存上下文然后放入队尾,并重新取出一个G执行;每个P会周期性地查看全局队列中是否有待运行的G并将其调度到M中执行。

工作量窃取:各个P中维护的G队列很可能是不均衡的;空闲的P会查询全局队列,若全局队列也空,则会从其他P中窃取G(一般每次取一半)。

协作式调度

Go scheduler 是 Go runtime 的一部分,运行在用户空间,其采用协作式调度(cooperating)。

Go scheduler的核心思想是:

  • 线程重用;
  • 限制同时运行(不包含阻塞)的线程数为 N(N 等于 CPU 的核数);
  • 线程私有的执行队列:
    • 线程阻塞时,可将 runqueues 传递给其他线程。
    • 线程空闲时,可从其他线程 stealing goroutine 来运行。

goroutine会在以下时机发生调度:

情形说明
关键字 gogo 创建一个新的 goroutine,Go scheduler 会考虑调度
GC由于进行 GC 的 goroutine 也需要在 M 上运行,因此肯定会发生调度
系统调用goroutine 进行系统调用时,会阻塞 M,所以它会被调度走,同时一个新的 goroutine 会被调度上来
内存同步访问atomic,mutex,channel 操作等会使 goroutine 阻塞,条件满足后会被重新调度;

系统调用

系统调用分为同步与异步,对不同的情况go有不同的策略。一般M数量会略大于P的个数(处理G产生系统调用)。

同步调用

对于同步调用:G1即将进入系统调用时,M1将释放P,让某个空闲的M2获取P并继续执行P队列中剩余的G(即M2接替M1的工作);M2可能来源于M的缓存池,也可能是新建的。当G1系统调用完成后,根据M1能否获取到P,将对G1做不同的处理:

  • 有空闲的P,则获取一个以继续执行G1;
  • 无空闲P,将G1放入全局队列,等待被其他P调度;M1进入缓冲池睡眠。
    GPM-syscall-sync
异步调用

对于异步调用:M 不会被阻塞,G 的异步请求会被“代理人” network poller 接手,G 也会被绑定到 network poller,等到系统调用结束,G 才会重新回到 P 上。M 由于没被阻塞,它因此可以继续执行 LRQ 里的其他 G。
GPM-syscall-asyn

scheduler的陷阱

Go scheduler 有一个后台线程在持续监控,一旦发现 goroutine 运行超过 10 ms,会设置 goroutine 的“抢占标志位”,之后调度器会处理。但是设置标志位的时机只有在函数“序言”部分,对于没有函数调用的就没有办法了。

func main() {
	var x int
	threads := runtime.GOMAXPROCS(0)
	for i := 0; i < threads; i++ {
		go func() {
			for { x++ }
		}()
	}
	time.Sleep(time.Second)
	fmt.Println("x =", x)
}

由于goroutine数量与M数量相等,所有goroutine启动后都进入了死循环中,没有调度出的机会;所以主线程一直没有机会执行。

运行结果是:在死循环里出不来,不会输出最后的那条打印语句。

为避免此类问题,需要减少死循环的goroutine数量:

func main() {
	var x int
	threads := runtime.GOMAXPROCS(0) - 1
	for i := 0; i < threads; i++ {
		go func() {
			for { x++ }
		}()


![img](https://img-blog.csdnimg.cn/img_convert/9ee6e6ab6fbabadb0c476fbcefee1a6a.png)
![img](https://img-blog.csdnimg.cn/img_convert/c542df7aa35854b58c27ccd775b877f0.png)
![img](https://img-blog.csdnimg.cn/img_convert/13b96b4453c4195a274d675a774bfa81.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618658159)**

*

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618658159)**

  • 30
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值