我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Golang Channel的底层结构是什么

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Golang Channel的底层结构是什么

这篇文章主要介绍“Golang Channel的底层结构是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Golang Channel的底层结构是什么”文章能帮助大家解决问题。

Golang 使用 Groutine 和 channels 实现了 CSP(Communicating Sequential Processes) 模型,channlesgoroutine 的通信和同步中承担着重要的角色。

GopherCon 2017 中,Golang 专家 Kavya 深入介绍了 Go Channels 的内部机制,以及运行时调度器和内存管理系统是如何支持 Channel 的,本文根据 Kavya 的 ppt 学习和分析一下 go channels 的原理,希望能够对以后正确高效使用 golang 的并发带来一些启发。

以一个简单的 channel 应用开始,使用 goroutine channel 实现一个任务队列,并行处理多个任务。

func main(){      //带缓冲的 channel      ch := make(chan Task, 3)      //启动固定数量的 worker      for i := 0; i< numWorkers; i++ {          go worker(ch)      }      //发送任务给 worker      hellaTasks := getTaks()      for _, task := range hellaTasks {          ch <- task      }      ...  }  func worker(ch chan Task){      for {         //接受任务         task := <- ch         process(task)      }  }

从上面的代码可以看出,使用 golang goroutinechannel 可以很容易的实现一个生产者-消费者模式的任务队列,相比 Java, c++简洁了很多。

channel 可以天然的实现了下面四个特性:

  • goroutine 安全

  • 在不同的 goroutine 之间存储和传输值 &ndash; 提供 FIFO 语义 (buffered channel 提供)

  • 可以让 goroutine block/unblock

那么 channel 是怎么实现这些特性的呢?下面我们看看当我们调用 make 来生成一个 channel 的时候都做了些什么。

make chan

上述任务队列的例子第三行,使用 make 创建了一个长度为 3 的带缓冲的 channel,channel 在底层是一个 hchan 结构体,位于 class="lazy" data-src/runtime/chan.go 里。

其定义如下:

type hchan struct {      qcount   uint           // total data in the queue      dataqsiz uint           // size of the circular queue      buf      unsafe.Pointer // points to an array of dataqsiz elements      elemsize uint16      closed   uint32      elemtype *_type // element type      sendx    uint   // send index      recvx    uint   // receive index      recvq    waitq  // list of recv waiters      sendq    waitq  // list of send waiters      // lock protects all fields in hchan, as well as several      // fields in sudogs blocked on this channel.      //      // Do not change another G's status while holding this lock      // (in particular, do not ready a G), as this can deadlock      // with stack shrinking.      lock mutex  }

make 函数在创建 channel 的时候会在该进程的 heap 区申请一块内存,创建一个 hchan 结构体,返回执行该内存的指针,所以获取的的 ch 变量本身就是一个指针,在函数之间传递的时候是同一个 channel。

hchan 结构体使用一个环形队列来保存 groutine 之间传递的数据(如果是缓存 channel 的话),使用**两个 list **保存像该 chan 发送和从该 chan 接收数据的 goroutine,还有一个 mutex 来保证操作这些结构的安全。

发送和接收

channel 发送和从 channel 接收数据主要涉及 hchan 里的四个成员变量,借用 Kavya ppt 里的图示,来分析发送和接收的过程。

Golang Channel的底层结构是什么

还是以前面的任务队列为例:

//G1  func main(){      ...      for _, task := range hellaTasks {          ch <- task    //sender      }      ...  }  //G2  func worker(ch chan Task){      for {         //接受任务         task := <- ch  //recevier         process(task)      }  }

其中 G1 是发送者,G2 是接收,因为 ch 是长度为 3 的带缓冲 channel,初始的时候 hchan 结构体的 buf 为空,sendx 和 recvx 都为 0,当 G1 向 ch 里发送数据的时候,会首先对 buf 加锁,然后将要发送的数据 copy 到 buf 里,并增加 sendx 的值,最后释放 buf 的锁。然后 G2 消费的时候首先对 buf 加锁,然后将 buf 里的数据 copy 到 task 变量对应的内存里,增加 recvx,最后释放锁。整个过程,G1 和 G2 没有共享的内存,底层通过 hchan 结构体的 buf,使用 copy 内存的方式进行通信,最后达到了共享内存的目的,这完全符合 CSP 的设计理念

Do not comminute by sharing memory;instead, share memory by communicating

一般情况下,G2 的消费速度应该是慢于 G1 的,所以 buf 的数据会越来越多,这个时候 G1 再向 ch 里发送数据,这个时候 G1 就会阻塞,那么阻塞到底是发生了什么呢?

Goroutine Pause/Resume

goroutine 是 Golang 实现的用户空间的轻量级的线程,有 runtime 调度器调度,与操作系统的 thread 有多对一的关系,相关的数据结构如下图:

Golang Channel的底层结构是什么

其中 M 是操作系统的线程,G 是用户启动的 goroutine,P 是与调度相关的 context,每个 M 都拥有一个 P,P 维护了一个能够运行的 goutine 队列,用于该线程执行。

当 G1 向 buf 已经满了的 ch 发送数据的时候,当 runtine 检测到对应的 hchan 的 buf 已经满了,会通知调度器,调度器会将 G1 的状态设置为 waiting, 移除与线程 M 的联系,然后从 P 的 runqueue 中选择一个 goroutine 在线程 M 中执行,此时 G1 就是阻塞状态,但是不是操作系统的线程阻塞,所以这个时候只用消耗少量的资源。

那么 G1 设置为 waiting 状态后去哪了?怎们去 resume 呢?我们再回到 hchan 结构体,注意到 hchan 有个 sendq 的成员,其类型是 waitq,查看源码如下:

type hchan struct {       ...       recvq waitq // list of recv waiters       sendq waitq // list of send waiters       ...   }   //   type waitq struct {       first *sudog       last *sudog   }

实际上,当 G1 变为 waiting 状态后,会创建一个代表自己的 sudog 的结构,然后放到 sendq 这个 list 中,sudog 结构中保存了 channel 相关的变量的指针(如果该 Goroutine 是 sender,那么保存的是待发送数据的变量的地址,如果是 receiver 则为接收数据的变量的地址,之所以是地址,前面我们提到在传输数据的时候使用的是 copy 的方式)

Golang Channel的底层结构是什么

当 G2 从 ch 中接收一个数据时,会通知调度器,设置 G1 的状态为 runnable,然后将加入 P 的 runqueue 里,等待线程执行。

Golang Channel的底层结构是什么

wait empty channel

前面我们是假设 G1 先运行,如果 G2 先运行会怎么样呢?如果 G2 先运行,那么 G2 会从一个 empty 的 channel 里取数据,这个时候 G2 就会阻塞,和前面介绍的 G1 阻塞一样,G2 也会创建一个 sudog 结构体,保存接收数据的变量的地址,但是该 sudog 结构体是放到了 recvq 列表里,当 G1 向 ch 发送数据的时候,runtime 并没有对 hchan 结构体题的 buf 进行加锁,而是直接将 G1 里的发送到 ch 的数据 copy 到了 G2 sudog 里对应的 elem 指向的内存地址!

Golang Channel的底层结构是什么

关于“Golang Channel的底层结构是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网行业资讯频道,小编每天都会为大家更新不同的知识点。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Golang Channel的底层结构是什么

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Golang Channel的底层结构是什么

这篇文章主要介绍“Golang Channel的底层结构是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Golang Channel的底层结构是什么”文章能帮助大家解决问题。Golang 使用
2023-06-26

golang channel底层原理是什么

Golang的channel底层原理是基于通信顺序进程(Communicating Sequential Processes,简称CSP)模型实现的。在Golang中,channel是一种用于在goroutine之间进行通信和同步的机制。
golang channel底层原理是什么
2024-02-29

Linux的底层体系结构是怎样的

本文小编为大家详细介绍“Linux的底层体系结构是怎样的”,内容详细,步骤清晰,细节处理妥当,希望这篇“Linux的底层体系结构是怎样的”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。计算机的工作模式对于一个计算机
2023-06-16

redis的五种数据类型底层数据结构是什么

redis 提供了五种数据类型,每种类型对应特定的底层数据结构:字符串:简单动态字符串(sds),优化二进制安全字符串存储。哈希:哈希表(dict),快速键值对存储。列表:双向链表或压缩列表(zip list),支持顺序访问和插入/删除操作
redis的五种数据类型底层数据结构是什么
2024-04-08

golang map底层实现原理是什么

Golang中的map是基于散列表(hash table)实现的。散列表是一种用于存储键值对的数据结构,它通过将键映射到数组的索引来实现高效的插入、查找和删除操作。具体来说,Golang中的map底层实现原理如下:Golang的map使用
2023-10-21

Internet四层结构指的是什么

这篇文章主要讲解了“Internet四层结构指的是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Internet四层结构指的是什么”吧!Internet的四
2023-02-24

web前端三层结构是什么

本篇内容介绍了“web前端三层结构是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!web前端的三层结构分别是:1、结构层,由HTML或X
2023-07-05

react底层的四大核心内容架构是什么

今天小编给大家分享一下react底层的四大核心内容架构是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。react提供定义
2023-06-29

HashMap的底层原理是什么

这篇文章将为大家详细讲解有关HashMap的底层原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。一:HashMap的节点:HashMap是一个集合,键值对的集合,源码中每个节点用No
2023-06-04

Vue的底层原理是什么

这篇文章主要介绍Vue的底层原理是什么,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Observer (数据劫持)核心是通过Obeject.defineProperty()来监听数据的变动,这个函数内部可以定义set
2023-06-29

gzip压缩文件底层结构及文件损坏的修复方法是什么

本文小编为大家详细介绍“gzip压缩文件底层结构及文件损坏的修复方法是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“gzip压缩文件底层结构及文件损坏的修复方法是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知
2023-06-04

Spring Boot的底层原理是什么

这篇文章主要讲解了“Spring Boot的底层原理是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Spring Boot的底层原理是什么”吧!1.基于你对springboot的理解描述
2023-06-27

golang输出结构体的方法是什么

在Go语言中,要输出结构体的方法有两种方式:1. 使用fmt包的Println、Printf或Print函数:这些函数可以直接打印出结构体的字段值。例如:```gopackage mainimport ("fmt")type Person
2023-08-31

linux中mutex的底层原理是什么

在Linux中,mutex的底层原理主要是基于原子操作和内核态的同步机制来实现的。具体来说,Linux中的mutex通常是通过spinlock(自旋锁)来实现的。当一个线程尝试获取mutex时,它会先尝试通过原子操作来修改mutex的状态
linux中mutex的底层原理是什么
2024-03-15

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录