这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记
第二节:Go 语言上手 - 工程实践
并发编程
Go语言实现并发编程的三座基石——
-
Goroutine
goroutine 是 Go语言中的轻量级线程实现,由 Go 运行时(runtime)管理。Go 程序会智能地将 goroutine 中的任务合理地分配给每个 CPU。关于任务的合理分配涉及到了GM模型以及GMP模型,采用的是一种协作式抢占的分配理念,其中G表示的就是goroutine,M表示内核级线程,P表示处理器等运行所需的资源。关于这方面,刘丹冰有一篇相关的博文描述的相当详细,想具体了解可以搜索一下。
简易来说,步骤如下:
1. 调用 go func()创建一个goroutine; 2. 新创建的G优先保存在P的本地队列中,如果P的本地队列已经满了就会保存在全局的队列中; 3. M需要在P的本地队列弹出一个可执行的G,如果P的本地队列为空,则先会去全局队列中获取G,如果全局队列也为空则去其他P中偷取G放到自己的P中 4. G将相关参数传输给M,为M执行G做准备 5. 当M执行某一个G时候如果发生了系统调用产生导致M会阻塞,如果当前P队列中有一些G,runtime会将线程M和P分离,然后再获取空闲的线程或创建一个新的内核级的线程来服务于这个P,阻塞调用完成后G被销毁将值返回; 6. 销毁G,将执行结果返回7. 当M系统调用结束时候,这个M会尝试获取一个空闲的P执行,如果获取不到P,那么这个线程M变成休眠状态, 加入到空闲线程中。 -
Channel
Channel出现的目的主要是为为Goroutine实现一种方便的通信方式,采用管道通信的方式
关于这个我们可以简单聊一下进程间通信PIC:
常说的进程间通信方式有六种,分别为:
管道通信 消息队列 共享内存/文件 信号 信号量 Socket管道通信是一种采用先进先出原则进行通信的通信方式
消息队列与管道通信类似,但是通信不及时,消息体大小、消息队列长度均有限制
共享内存存在资源同步及互斥问题
信号量是专门解决同步互斥问题的
信号一般不由用户来使用,不做说明
Socket常见于跨网络通信
-
Sync
sync提供了基础的异步操作方法,包括互斥锁mutex,并发等待组waitgroup,确保函数只被执行一次的原语sync.Once等,还提供了一些并发安全的工具,如sync.Map(一个并发安全的map)、sync.pool(并发池)。可用于解决变量的并发安全问题