Go语言goroutine与GMP模型

并行与并发

并发:同一时段内执行多个任务

并行:同一时刻执行多个任务

Go语言并发通过goroutine实现。goroutine类似于线程,属于用户态的线程,可以根据需要创建成千上万个goroutine并发工作。goroutine由Go语言的运行时(runtime)调度完成,而线程由操作系统调度完成。

Go语言提供channel在多个goroutine进行通信。goroutinechannel是Go语言秉承的CSP(Communicating Sequential Process)并发模式的重要实现基础。

goroutine与GMP模型
可增长的栈

内核态线程一半都有固定的栈内存(通常有2MB),一个goroutine的栈在其声明周期开始时只有很小的栈(典型情况下为2KB),goroutine的栈是不固定的,其可以按需增大和缩小。goroutine的栈大小可以达到1GB。在GO语言中一次创建十万左右的goroutine也是可以的。

goroutine调度模型GMP

GMP是Go语言运行时层面的实现,是Go语言自己实现的一套调度系统。区别于操作系统调度OS线程。

  • G :单个goroutine运行时,里面除了存放本goroutine信息外,还存有于其所在P的绑定的的信息。
  • M(Machine):Go运行时(runtime)对操作系统内核线程的虚拟,M与内核线程一般是一一对应的关系,一个goroutine最终都要放到M上执行。
  • P:管理一组goroutine队列,P里面会存储当前goroutine运行时的上下文环境(函数指针,堆栈地址以及地址边界),P会对自己管理的giroutine队列最一些调度,当自己的队列消费完就去全局队列中取,如果全局队列也消费完毕就去其P他队列中抢任务。

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

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

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

GOMAXPROCS

Go运行时的调度器使用GOMAXPROCS参数来确定需要使用多少个OS线程来同时执行Go代码。默认值是机器上的CPU核心数。(GOMAXPROCS是m:n调度中的n)

GO语言可以通过使用runtime.GOMAXPROCS()设置当前程序并发时占用的逻辑核心数.

GO1.5版本之前,默认使用单核心执行.Go1.5版本中之后,默认使用全部的CPU逻辑核心数.

Go语言中的操作系统线程和goroutine的关系:
  1. 一个操作系统线程对应用户态多个goroutine
  2. go程序可以同时使用多个操作系统线程
  3. goroutine和OS线程时多对多的关心,即m:n
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值