go一个协程安全协程调度的问题

简介: go一个协程安全协程调度的问题

看一段代码,请问输出什么?

package main
import "time"
func main() {
   var testNum = 0
   go func() {
      time.Sleep(10000)
      testNum = 1
   }()
   for testNum == 0 {
      if testNum==1 {
         println("testNum=1")
      }
      println("testNum=",testNum)
   }
   println("loop end.")
}

输出结果:

image.png


从结果中,能发现2个问题:

1:循环确实结束了,说明testNum!=0

2:timeNum!=0,则说明TetsNum = 1赋值成功了,但是里面的=1判断却没有打印,为什么呢?

这是因为 main协程和 子协程共享变量造成的问题,主要执行流程如下:

package main
import "time"
func main() {
   var testNum = 0
   go func() {
      time.Sleep(10000)  //隔了10000纳秒
      testNum = 1  //赋值为1
   }()
   for testNum == 0 { 
      //在10000纳秒之前,这个值都为0
      if testNum==1 { //在执行完这次判断后,testNum才更新为1,此时已经没有条件进入此循环,所以此代码永远不执行
         println("testNum=1") 
      }
      println("testNum=",testNum)
      //10000纳秒之前,为0
      //在执行完这条后,testNum突然被更新为了1,所以不会进行下一次循环
   }
   println("loop end.")
}

那么,这里面又涉及到了一个新的问题:

为什么是刚好在执行完一次循环之后,才刚好轮到testNum=1,而不是在执行前之前轮转到呢?

这就涉及到了go的协程调度问题了,具体是怎么调度的呢?

go的协程调度

go的协程调度为 [典藏版] Golang 调度器 GMP 原理与调度全分析

简单说明:

G:协程

P:协程队列

M:执行的线程

- 在运行时,G必须绑定在P上,P必须在M上才可以运行程序,而cpu调度器执行的是M,也就是有多少核心,或者有多少个M,就可以同时运行多少个M/G

- 多个P绑定在M上,在发生syscall或者io阻塞时,会自动挂起,P将切换其他G执行,当G运行时间超过10ms(1.14后加入),会自动切换成其他协程

理解这2句话就够了,我们回到代码:

image.png

因为加了输出,导致了协程一定会切换,所以100%可以复现上面的问题,如果这句输出放到上面去运行,则变成了100%输出 testNum=1:

image.png

刚刚我们看到了GMP的第一点,有多少个P,就应该有多少个M/G同时运行,那么问题来了,为什么上面的2个协程没有并行呢?

GMP的另一个特点:

- M可能会有多个P绑定,当P阻塞后,M将绑定其他P进行执行

- P会有多个G绑定,当一个G阻塞后,将获取一个新的G进行运行

- 如果P的G已经超出数量后,将会分一半给其他的P

- 如果P没有可以执行的G后,将会偷其他P的G

在示例代码,由于是先执行的go func,sleep(协程2),所以P在执行main开始之后,立即开始执行协程2,同时由于协程2 sleep阻塞,所以切回main协程运行,在刚执行的时候,由于并没有繁忙情况,所以没有启用P2和M2进行运行,所以没有实现并行

之前把P和M弄混了,现在改回来了,嘿嘿

目录
相关文章
|
3月前
|
安全 Go
Golang语言goroutine协程并发安全及锁机制
这篇文章是关于Go语言中多协程操作同一数据问题、互斥锁Mutex和读写互斥锁RWMutex的详细介绍及使用案例,涵盖了如何使用这些同步原语来解决并发访问共享资源时的数据安全问题。
101 4
|
4月前
|
Go 调度 开发者
[go 面试] 深入理解进程、线程和协程的概念及区别
[go 面试] 深入理解进程、线程和协程的概念及区别
|
5月前
|
Java Go 调度
GO 协程
GO 协程
45 0
|
2月前
|
安全 Go 调度
探索Go语言的并发模式:协程与通道的协同作用
Go语言以其并发能力闻名于世,而协程(goroutine)和通道(channel)是实现并发的两大利器。本文将深入了解Go语言中协程的轻量级特性,探讨如何利用通道进行协程间的安全通信,并通过实际案例演示如何将这两者结合起来,构建高效且可靠的并发系统。
|
4月前
|
Go 调度 C++
Go(1)——调度的本质
Go(1)——调度的本质
|
4月前
|
存储 安全 程序员
|
4月前
|
监控 Devops 测试技术
|
4月前
|
NoSQL Unix 编译器
Golang协程goroutine的调度与状态变迁分析
文章深入分析了Golang中goroutine的调度和状态变迁,包括Grunnable、Gwaiting、Grunning和Gsyscall等状态,以及它们之间的转换条件和原理,帮助理解Go调度器的内部机制。
56 0
|
4月前
|
人工智能 Go
Go 等待协程完成
Go 等待协程完成
32 0
|
4月前
|
存储 安全 编译器