C++选手快速上手Go(3) | 青训营笔记

122 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记

第二节:Go 语言上手 - 工程实践

并发编程

Go语言实现并发编程的三座基石——

  1. 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变成休眠状态, 加入到空闲线程中。
    
  2. Channel

    Channel出现的目的主要是为为Goroutine实现一种方便的通信方式,采用管道通信的方式

    关于这个我们可以简单聊一下进程间通信PIC:

    常说的进程间通信方式有六种,分别为:

    管道通信
    消息队列
    共享内存/文件
    信号
    信号量
    Socket
    

    管道通信是一种采用先进先出原则进行通信的通信方式

    消息队列与管道通信类似,但是通信不及时,消息体大小、消息队列长度均有限制

    共享内存存在资源同步及互斥问题

    信号量是专门解决同步互斥问题的

    信号一般不由用户来使用,不做说明

    Socket常见于跨网络通信

  3. Sync

    sync提供了基础的异步操作方法,包括互斥锁mutex,并发等待组waitgroup,确保函数只被执行一次的原语sync.Once等,还提供了一些并发安全的工具,如sync.Map(一个并发安全的map)、sync.pool(并发池)。可用于解决变量的并发安全问题