Go语言学习路

Goroutine协程

2021-08-06  本文已影响0人  TZX_0710

Goroutine

Go语言中的goroutine概念类似于线程,但goroutine是由Go的运行时调度和管理的。Go程序会智能地将goroutine中的任务合理分配给每个CPU。Go语言之所以被称为现代化地编程语言,是因为语言层面内置了调度和上下文切换机制。

在Go语言中不需要自己去写线程,进程 ,协程,技能包里面只有一个goroutine当 需要某个任务并发执行的时候,只需要把这个任务包装成一个函数,再开启一个goroutine去执行就可以了。

使用goroutine

Go语言中使用goroutine非常简单,只需要在调用函数的时候在前面加上关键字,就可以为一个函数创建一个goroutine

一个goroutine必定对应一个函数,可以创建多个goroutine去执行相同的函数

启动单个goroutine

package main

import "fmt"

func hello(){
  fmt.Println("hello hello hello")
}
func main() {
  go hello() //运行多次可能会发现 有时候只打印了main方法一行。原因是在程序启动时候。Go程序就会为main函数默认创建一个goroutine。当main函数返回的时候该goroutine就结束了。所有在main函数中启动的goroutine函数会一天结束。所以有时候goroutine还没执行到hello 函数就已经没了
  fmt.Println("main main main")
    //可以采用time.Sleep(time.Second)//停顿一毫秒的方式等待一下 然后在运行查看
}

启动多个goroutine

在Go语言中实现并发只需要go func()就可以实现

func hello(i int) {
   fmt.Println("hello hello hello", i)
}
func main() {
   for i := 0; i < 10; i++ {
      go hello(i)
   }
   fmt.Println("main main main")
   time.Sleep(time.Second) //停顿一毫秒
}
hello hello hello 0
main main main
hello hello hello 5
hello hello hello 1
hello hello hello 4
hello hello hello 6
hello hello hello 7
hello hello hello 8
hello hello hello 2
hello hello hello 3
hello hello hello 9

通过上面的结果可以发现打印的结果顺序不一致,这是因为10个goroutine是并发执行的而goroutine的调度是随机的

注意

如果主协程退出了,其他任务不再执行。当主协程返回的时候,goroutine就都结束了。

goroutine的栈大小

OS线程(操作系统线程)一般都有固定的栈内存(通常为2MB)一个goroutine的栈其在生命周期开始时只有很小的栈(典型情况瞎时2KB),goroutine的栈不是固定的,它可以按需增大和缩小,goroutine的栈大小限制可以达到1GB.虽然极少会用到这个大。所以在Go语言中一次性创建10w左右的goroutine时可行的。

goroutine 调度

GPM时GO语言运行时的实现,是Go实现的一套调度系统。区别于操作系统调度OS线程

  • G就是goroutine。里面除了存放本goroutine信息外还有与所在P的绑定等信息
  • P管理者一组goroutine队列,P里面会存储当前goroutine运行的上下文环境,P会对自己管理的goroutine队列做一些调度当自己队列消费完了就去全局队列里面取。如果全局队列里也消费完了就去其他P里面去抢任务。
  • M是Go运行时对操作系统内核线程的的虚拟,M与内核线程一般是--映射的管理,一个groutine最终是要放到M上执行的。

P与M一般是一一对应的。P管理一组G挂在在M上运行。当一个G长久阻塞在一个M上时,runtime会新建一个M,阻塞G所在的P会把其他的G挂在在新建的M上。当旧的G阻塞完成或者认为其已经死掉时回收旧的M。

P的个数是通过runtime.GOMAXPROCS设定(最大256).GO1.5版本之后默认为物理线程数。在并发量大的时候会增加一些P和M,但不会太多,切换频繁的话得不偿失。

上一篇 下一篇

猜你喜欢

热点阅读