Go 语言入门指南:基础语法和常用特性解析

65 阅读4分钟

在上文中介绍了golang的基本特性及数据结构,本文集中讲解golang的并发机制

golang的并发机制

golang在内核级线程基础上提供特有的两级线程模型。 logo:不要用共享内存的方式进行通信。作为替代,应该以通信作为手段来共享内存。 image.png golang线程实现模型: 三个核心元素:M(machine,一个M代表一个内核线程称为“工作线程”),P(processor,一个P代表执行一个Go代码片段所必需的资源,“上下文环境”),G(goroutine,一个G代表一个Go代码片段,前者是对后者的一种封装) 内核M + 上下文环境P 形成G运行环境,每个P 包含一个可运行的G的队列。 image.png 约定:当前运行G的M叫做“当前M”, 与之关联的P称为“本地P”。 M: image.png g0: M拥有的特殊G,它管辖的内存为M的调度栈(系统栈),执行调度、垃圾回收、栈管理方面的任务。 gsignal:专用与处理信号的G,管辖信号栈。 M创建之初会加入到M列表,此时设置起始函数和预联的p,然后运行时系统为这个M专门创建一个新的内核线程并与之关联。系统通过全局M列表获取到所有M的信息,同时防止M被当作垃圾回收掉。起始函数专用于运行时系统使用这个M执行系统监控任务或者垃圾回收任务,一旦被设置就不会执行后续流程。否则就会和预联P完成关联并准备执行其他任务。 运行时系统在停止M的时候会把它放入调度器的空闲M列表。 M的自旋转状态:表示M还没有找到G来运行,退出条件:找到了可运行的G或者始终没找到G而需要停止M。一般调度器会尽量保证有一个自旋M存在,新G到来时会优先使用自旋M而不是新启动一个M或者恢复一个停止的M。

P: Go的运行时系统会适时让P与不同的M建立或断开关联。全局P列表包含了当前运行时系统创建的所有P,当P不再与M关联时会被放入空闲P列表中(前提是它的可运行G列表为空)。 每个P都有一个可运行G队列(最大为256,队列满的时候将一半的G移动到调度器的可运行G队列中)和自由G列表(包含一些已经运行完成的G,随着G完成数目增多部分G会转移到调度器的自由G列表中)。P的自由G和调度器的自由G相当于go函数的代码段封装,仅当它们不够时才创建新的G,以最大化G的复用。 P的状态: Pidle: 当前P未与任何M关联 Prunning: 当前P正在与某个M关联 Psyscall: 当前P中运行的G正在进行系统调用 Pgcstop:运行时系统需要停止调度 Pdead:当前P已经不会再被使用 image.png p 的最大数目默认等于正在运行当前Go程序的逻辑cpu数目,可通过runtime.GOMAXPROCS函数改变。(两项检查:最大值不超过256、正整数并且与旧值不同)。修改时停止调度器的工作,对全局P列表中的前I(新值)个P进行检查和初始化,不足的补充,多的进行清理。 image.png G: 一个G代表一个goroutine,go语句会变成对内部函数newproc的调用,相当于提交了一个并发任务。运行时系统有一个全局G列表,所有新建的G会第一时间加入该列表中。本地P的runnext字段保存下一个运行的G,新建一个G时,如果该字段已有一个G会被踢到该P的可运行G队列末尾,被这个G取代;如果可运行G队列已满,则这个G只能被追加到调度器的可运行G队列中。 G的状态: Gidle:当前G刚被新分配,但未被初始化 Grunnable:当前G正在可运行队列中等待执行 Grunning:当前G正在运行 Gsyscall:当前G正在执行某个系统调用 Gwaiting:当前G正在阻塞 Gdead:当前G正在闲置 Gcopystack:当前G的栈正在被移动 image.png