goroutine、channel以及GMP模型的原理深度解析【万字分析】

文章目录

  • 前言
  • 一、channel的底层原理
    • 1、底层数据结构
    • 2、创建关闭
    • 3、发送接受
  • 二、goruntine的底层原理
    • 1、线程的代价
    • 2、goruntine的底层原理
    • 3、状态
    • 4、创建、运行与退出
    • 3、阻塞与唤醒
  • 三、GMP模型的概述与发展
    • 1、GM模型
    • 2、GMP模型组成部分
    • 3、G(Goroutine)
    • 4、M(Machine)
    • 5、P(Processor)
    • 6、Sched:调度器结构
  • 四、GMP调度原理
    • 1、被调度对象
    • 2、调度流程
    • 3、调度时机
    • 4、work stealing 机制
    • 5、hand off 机制
    • 6、抢占式调度
      • 1、基于协作的抢占式调度
      • 2、基于信号的抢占式调度

前言

goroutine、channel以及GMP模型是学习golang绕不开的部分,之前学习golang的时候对这一块的理解不够深入,本文将深度分析并且总结他们的底层原理。

一、channel的底层原理

channel又称为管道,用于数据传递或数据共享,其本质是一个先进先出的队列,使用goroutine+channel进行数据通讯简单高效,同时也线程安全,多个goroutine可同时修改一个channel,不需要加锁。CSP(Communicating Sequential Process)并发模型,就是通过 goroutine 和 channel 来实现的)
channel有哪些状态:
**未初始化的状态,**只进行了声明,或者手动赋值为nil。

**active:**正常的channel,可读或者可写。

closed:已关闭,channel的值不是nil,关闭的状态的channel仍然可以读值(取值),但不能写值(会报panic: send on closed channel),nil状态的channel是不能close(panic: close of nil channel)的。如果关闭后的 channel 没有数据可读取时,将得到零值,即对应类型的默认值

1、底层数据结构

通过var声明或者make函数创建的channel变量是一个存储在函数栈帧上的指针,占用8个字节,指向堆上的hchan结构体
buf指向一个底层的循环数组,只有设置为有缓存的channel才会有buf
sendx和recvx分别指向底层循环数组的发送和接收元素位置的索引
sendq和recvq分别表示发送数据的被阻塞的goroutine和读取数据的goroutine,这两个都是一个双向链表结构
sendq和recvq 的结构为等待队列类型,sudog是对goroutine的一种封装 是双向链表,包含一个头结点和一个尾结点,每个节点是一个sudog结构体变量,记录哪个协程在等待,等待的是哪个channel,等待发送/接收的数据在哪里goroutine、channel以及GMP模型的原理深度解析【万字分析】_第1张图片

type hchan struct {
   
   
    qcount   uint           // channel中的元素个数
    dataqsiz uint           // channel中循环队列的长度
    buf      unsafe.Pointer // channel缓冲区数据指针
    elemsize uint16            // buffer中每个元素的大小
    closed   uint32            // channel是否已经关闭,0未关闭
    elemtype *_type // channel中的元素的类型
    sendx    uint   // channel发送操作处理到的位置
    recvx    uint   // channel接收操作处理到的位置
    recvq    waitq  //读等待队列 等待接收的sudog(sudog为封装了goroutine和数据的结构)队列由于缓冲区空间不足而阻塞的goroutine列表
    sendq    waitq  //写等待队列  等待发送的sudog队列,由于缓冲区空间不足而阻塞的goroutine列表

    lock mutex   // 一个轻量级锁
}

type waitq struct {
   
   
   first *sudog
   last  *sudog
}

type sudog struct {
   
   
    g *g
    next *sudog
    prev *sudog
    elem unsafe.Pointer 
    c        *hchan 
    ...
}




2、创建关闭

创建channel 有两种,一种是带缓冲的channel,一种是不带缓冲的channel

// 带缓冲
ch := make(chan int, 3)
// 不带缓冲
ch := make(chan int)

创建时的策略:
如果是无缓冲的 channel,会直接

你可能感兴趣的:(算法,开发语言,golang)