Go基础学习-goroutine与线程

goroutine与线程

  • Go语言的并发通过goroutine实现
  • goroutine类似于线程,属于用户态的线程,一个操作系统线程对应用户态多个goroutine。
  • goroutine是由Go语言的运行时(runtime)调度完成,而线程是由操作系统调度完成
  • oroutine和channel是 Go 语言秉承的CSP(通过通信来共享内存)(Communicating Sequential Process)并发模式的重要实现基础
  • OS线程(操作系统线程)一般都有固定的栈内存(通常为2MB)
  • 一个goroutine的栈在其生命周期开始时只有很小的栈(典型情况下2KB);且栈不是固定的,他可以按需增大和缩小;最大可达到1GB
//程序启动之后也会创建一个主goroutine(main) 去执行
//goroutine 什么时候结束?goroutine对应的函数结束了,goroutine也就结束了
//main 函数执行完了;由main函数创建的那些goroutine也就结束了
func main() {
	for i := 0; i < 100; i++ {
		//go hello(i) //开启一个单独的goroutine去执行hello函数(任务)
		go func() {
			fmt.Println(i) //相当于闭包,从外面拿取i值;而不是传进来的值;就会导致;同一数字多次打印执行
		}()
	}
	fmt.Println("main")
	time.Sleep(time.Second * 1) //自定义时间来等待goroutine结束;不够规范;具体的需要调用wg包来实现;详见waitGroup文件
	//main函数结束后 由main函数启动的goroutine也都会结束
}

func hello(i int) {
	fmt.Println("hello word", i)
}

goroutine调度

详见:深入Golang调度器之GMP模型

  • goroutine调度:GPM(是Go语言运行时(runtime)层面的实现)

    1. G:就是个goroutine的,里面除了存放本goroutine信息外 还有与所在P的绑定等信息。
    2. P:管理着一组goroutine的队列,P里面会存储当前goroutine运行的上下文环境(函数指针,堆栈地址及地址边界),P会对自己管理的goroutine队列做一些调度(比如把占用CPU时间较长的goroutine暂停、运行后续的goroutine等等)当自己的队列消费完了就去全局队列里取,如果全局队列里也消费完了会去其他P的队列里抢任务。
    3. M:(machine)是Go运行时(runtime)对操作系统内核线程的虚拟, M与内核线程一般是一一映射的关系, 一个groutine最终是要放到M上执行的;
      在这里插入图片描述
  • P与M一般也是一一对应的。他们关系是: P管理着一组G挂载在M上运行。当一个G长久阻塞在一个M上时,runtime会新建一个M,阻塞G所在的P会把其他的G 挂载在新建的M上。当旧的G阻塞完成或者认为其已经死掉时 回收旧的M。

  • Go语言相比起其他语言的优势在于OS线程是由OS内核来调度的,goroutine则是由Go运行时(runtime)自己的调度器调度的,这个调度器使用一个称为m:n调度的技术(复用/调度m个goroutine到n个OS线程)。其一大特点是goroutine的调度是在用户态下完成的, 不涉及内核态与用户态之间的频繁切换,包括内存的分配与释放,都是在用户态维护着一块大的内存池, 不直接调用系统的malloc函数(除非内存池需要改变),成本比调度OS线程低很多。 另一方面充分利用了多核的硬件资源,近似的把若干goroutine均分在物理线程上, 再加上本身goroutine的超轻量,以上种种保证了go调度方面的性能。

  • P数量的设置:是通过runtime.GOMAXPROCS设定(最大256;默认值是机器上的CPU核心数);(Go1.5版本之前,默认使用的是单核心执行;Go1.5版本之后默认为物理线程数)

var wg sync.WaitGroup

func main() {
	//默认使用物理线程数;当指定1个OS线程时;下面函数按照顺序执行;当使用os线程数大于1时,下面的函数会同时执行
	runtime.GOMAXPROCS(1)
	fmt.Println(runtime.NumCPU()) //查看物理线程数,即CPU的逻辑核心数
	wg.Add(2)
	go a()
	go b()
	wg.Wait()
}

func a() {
	defer wg.Done()
	for i := 0; i < 10; i++ {
		fmt.Printf("A:%d\n", i)
	}
}

func b() {
	defer wg.Done()
	for i := 0; i < 10; i++ {
		fmt.Printf("B:%d\n", i)
	}
}

猜你喜欢

转载自blog.csdn.net/wzb_wzt/article/details/107387368