【Go】Channel底层实现 ②

文章目录

    • channel底层实现
      • channel发送、接收数据
      • 有缓冲 channel
        • channel 先写再读
        • channel 先读再写(when the receiver comes first)
      • 无缓冲channel
      • channel存在3种状态:

channel底层实现

// channel 类型定义
type hchan struct {// channel 中的元素数量, lenqcount   uint           // total data in the queue// channel 的大小, capdataqsiz uint           // size of the circular queue// channel 的缓冲区,环形数组实现 buf      unsafe.Pointer // points to an array of dataqsiz elements// 单个元素(数据类型)的大小elemsize uint16// closed 标志位closed   uint32// 元素的类型elemtype *_type // element type 指向类型元数据 (内存复制、垃圾回收等机制依赖数据的类型信息)// send 和 recieve 的索引,用于实现环形数组队列(用于记录 交替读写的下标位置)sendx    uint   // send indexrecvx    uint   // receive index// recv goroutine 等待队列 想读取数据但又被阻塞住的 goroutine 队列recvq    waitq  // list of recv waiters// send goroutine 等待队列 想发送数据但又被阻塞住的 goroutine 队列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
}// 等待队列的链表实现
type waitq struct {    first *sudog       last  *sudog       
}// in src/runtime/runtime2.go
// 对 G 的封装
type sudog struct {// The following fields are protected by the hchan.lock of the// channel this sudog is blocking on. shrinkstack depends on// this for sudogs involved in channel ops.g          *gselectdone *uint32 // CAS to 1 to win select race (may point to stack)next       *sudogprev       *sudogelem       unsafe.Pointer // data element (may point to stack)// The following fields are never accessed concurrently.// For channels, waitlink is only accessed by g.// For semaphores, all fields (including the ones above)// are only accessed when holding a semaRoot lock.acquiretime int64releasetime int64ticket      uint32parent      *sudog // semaRoot binary treewaitlink    *sudog // g.waiting list or semaRootwaittail    *sudog // semaRootc           *hchan // channel
}

在这里插入图片描述

sendq 和 recvq 存储了当前 Channel 由于缓冲区空间不足而阻塞的 Goroutine 列表,这些等待队列使用双向链表 waitq 表示,链表中所有的元素都是 sudog 结构:

type waitq struct {first *sudoglast  *sudog
}

一个通道发送和接收通道,默认是阻塞的。

如果没有缓冲区,单纯的往其中放入元素立马就会进入阻塞状态,必须有其他的线程从其中取走元素。通俗的讲要有一个线程不断的取这个管道的元素,才能往其中放入元素。它就像一个窄窄的门框,进去就得出来。

而有一个缓冲区的管道想一段地道,放入的元素不会马上进入阻塞状态,只有第二个准备进入而第一个还没有进入的情况下才会阻塞。

package mainimport ("fmt""time"
)func main() {intChan := make(chan int, 1)go func() {for {v, ok := <-intChanif !ok {break}else{fmt.Println(v)}}}()intChan <- 1close(intChan)time.Sleep(time.Second * 1)
}

Channel是异步进行的。

channel发送、接收数据

// G1
func main() { ... for _, task := range tasks { taskCh <- task} ... 
}// G2
func worker() { for { task := <-taskChprocess(task) } 
}

有缓冲 channel

channel 先写再读

这一次会优先判断缓冲数据区域是否已满,如果未满,则将数据保存在缓冲数据区域,即环形队列里。

如果已满,G1 暂时被挂在了 recvq ,让G1调gopark()休眠起来, G1与M解绑。

请添加图片描述

当 G2 要读取数据时,会优先从缓冲数据区域去读取,并且在读取完后,会检查 sendq 队列,如果 goroutine 有等待队列,则会将它上面的 data 补充到缓冲数据区域,并且也对其设置 goready 函数。同时设置 G1 goready 函数,G1状态从waitting改为runnable,调度到本地队列,等待下次调度运行。

请添加图片描述

在这里插入图片描述

channel 先读再写(when the receiver comes first)

G2 先读,但没数据,暂时被挂在了 recvq 队列,然后休眠起来。

G1 在写数据时,发现 recvq 队列有 goroutine 存在,于是直接将数据发送给 G2。同时设置 G2 goready 函数,等待下次调度运行。

G2因为有runnext指针,因为亲和性的原因优先级较高,会把G2调度到原来的P local quene中(p.runext指针)

所以说go语言的goroutine调度是协作式的,你阻塞靠别人唤醒, 因为由runtime实现

请添加图片描述

On resuming, G2 does not need to acquire channel lock and manipulate the buffer. Also, one fewer memory copy.

优点:不重复入队出队,没有锁的开销, 减少一次内存拷贝开销,效率很高

无缓冲channel

跟有缓冲情况类似

channel存在3种状态:

  • nil,未初始化的状态,只进行了声明,或者手动赋值为nil
  • active,正常的channel,可读或者可写
  • closed,已关闭,千万不要误认为关闭channel后,channel的值是nil

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/652054.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【vue3源码】vue源码探索之旅:项目介绍

简言 记录下我眼中的vue源码项目。 gitHubvue3项目仓库 项目要求: vue版本 3.4.15nodeV18.12.0以上使用pnpm包管理器vitest测试框架Vue3 vue3是渐进式JavaScript框架,易学易用,性能出色,适用场景丰富的 Web 前端框架。 Vue 是一个框架,也是一个生态。其功能覆盖了大部分…

解决 github.com port 443: Timed out 的问题

国内访问github.com总是那么不竟如人意&#xff0c;时而无法加载网页&#xff0c;时而等我们抽完了一根烟后&#xff0c;它还处于转圈的状态。 虽然国内有gitee.com等诸多的代码托管平台&#xff0c;但却鲜有国人愿意去呢&#xff1f;其中的缘由&#xff0c;想必也不用我多说&a…

openssl3.2 - 测试程序的学习 - test\aesgcmtest.c

文章目录 openssl3.2 - 测试程序的学习 - test\aesgcmtest.c概述笔记能学到的流程性内容END openssl3.2 - 测试程序的学习 - test\aesgcmtest.c 概述 openssl3.2 - 测试程序的学习 aesgcmtest.c 工程搭建时, 发现没有提供 test_get_options(), cleanup_tests(), 需要自己补上…

Strassen矩阵乘法的C语言算法实现

矩阵乘法是高等代数中的重要基本运算&#xff0c;本文将介绍Strassen矩阵乘法的基本原理和用C语言进行算法实现的过程。 1. 一般矩阵乘法 首先&#xff0c;我们来看一下一般矩阵乘法的计算过程。 矩阵 A [ a 11 a 12 … a 1 n a 21 a 22 … a 2 n … … … … a n 1 a n 2 …

【算法】观光(求次短路,Dijkstra)

题目 “您的个人假期”旅行社组织了一次比荷卢经济联盟的巴士之旅。 比荷卢经济联盟有很多公交线路。 每天公共汽车都会从一座城市开往另一座城市。 沿途汽车可能会在一些城市&#xff08;零或更多&#xff09;停靠。 旅行社计划旅途从 S 城市出发&#xff0c;到 F 城市结…

美赛注意事项

2024年1月27日 &#xff1a; 赖维杰 同学分享 1、最后的展现必须要漂亮&#xff08;绘图、呈现&#xff09; 李维情 西北建模王 论文位&#xff08;核心&#xff09;必须清楚建模位、编程位知道做了些什么 常见模型&#xff1a; 1、看真题&#xff0c;读往年论文&#xff0c;选…

在IntelliJ IDEA中通过Spring Boot集成达梦数据库:从入门到精通

目录 博客前言 一.创建springboot项目 新建项目 选择创建类型​编辑 测试 二.集成达梦数据库 添加达梦数据库部分依赖 添加数据库驱动包 配置数据库连接信息 编写测试代码 验证连接是否成功 博客前言 随着数字化时代的到来&#xff0c;数据库在应用程序中的地位越来…

pytorch-metric-learning度量学习工具官方文档翻译

基于Pytorch实现的度量学习方法 开源代码&#xff1a;pytorch-metric-learning官网文档&#xff1a;PyTorch Metric Learning官方文档 度量学习相关的损失函数介绍&#xff1a; 度量学习DML之Contrastive Loss及其变种度量学习DML之Triplet Loss度量学习DML之Lifted Structu…

RDMA vs InfiniBand 网卡接口如何区分?

(该架构图来源于参考文献) 高性能计算网络&#xff0c;RoCE vs. InfiniBand该怎么选&#xff1f; 新 RoCEv2 标准可实现 RDMA 路由在第三层以太网网络中的传输。RoCEv2 规范将用以太网链路层上的 IP 报头和 UDP 报头替代 InfiniBand 网络层。这样&#xff0c;就可以在基于 IP…

Android (6) 弹窗 onJsAlert,onJsConfirm,onJsPrompt

目录 1 网页的3种弹窗 1.1 Alert警示弹窗 1.2 Confirm确认弹窗 1.3 Prompt输入弹窗 2 WebView支持弹窗 2.1 onJsAlert 2.2 onJsConfirm 2.3 onJsPrompt AndroidApp内嵌一个WebView用于承载网页,WebView会监听拦截网页的3种弹窗(Alert,Confirm,Prompt),如果不做任何处理…

Java算法---递归算法基础介绍

目录 一、递归算法 二、递归算法的典型例子 &#xff08;1&#xff09;阶乘 &#xff08;2&#xff09;二分查找 &#xff08;3&#xff09;冒泡排序 &#xff08;4&#xff09;插入排序 一、递归算法 计算机科学中&#xff0c;递归是一种解决计算问题的方法。其中解决方案…

GM/T 0018-2012 设备接口描述笔记

GM/T 0018-2012 设备接口描述笔记 文章目录 GM/T 0018-2012 设备接口描述笔记6. 设备接口描述6.1 密码设备应用接口在公钥密码基础设施应用技术体系框架中的位置6.2 设备管理类函数6.3 密钥管理类函数6.4 非对称算法运算类函数6.5 对称算法运算类函数6.6 杂凑运算类函数6.7 用户…

ServletResponse接口

ServletResponse接口 ServletContext接口向servlet提供关于其运行环境的信息。上下文也称为Servlet上下文或Web上下文,由Web容器创建,用作ServletContext接口的对象。此对象表示Web应用程序在其执行的上下文。Web容器为所部署的每个Web应用程序创建一个ServletContext对象。…

NineData支持制定安全、可靠的SQL开发规范

在和数据库打交道中&#xff0c;不管是数据库管理员&#xff08;DBA&#xff09;还是开发人员&#xff0c;经常会做一些CURD操作。因为每个人对数据库的了解程度不一样&#xff0c;所以在项目上线时&#xff0c;往往还需要专职人员对数据库的CURD操作进行审核&#xff0c;确保C…

vue3+naiveUI二次封装的v-model 联动输入框

根据官网说明使用 源码 <template><div class"clw-input pt-3"><n-inputref"input":value"modelValue":type"type":title"title"clearable:disabled"disabled":size"size"placeholder&…

RISC-V常用汇编指令

RISC-V寄存器表&#xff1a; RISC-V和常用的x86汇编语言存在许多的不同之处&#xff0c;下面将列出其中部分指令作用&#xff1a; 指令语法描述addiaddi rd,rs1,imm将寄存器rs1的值与立即数imm相加并存入寄存器rdldld t0, 0(t1)将t1的值加上0,将这个值作为地址&#xff0c;取…

【JaveWeb教程】(32)SpringBootWeb案例之《智能学习辅助系统》的详细实现步骤与代码示例(5)文件上传的实现

目录 SpringBootWeb案例052. 文件上传2.1 简介2.2 本地存储 SpringBootWeb案例05 前面我们已经实现了员工信息的条件分页查询以及删除操作。 关于员工管理的功能&#xff0c;还有两个需要实现新增和修改员工。 本节的主要内容&#xff1a; 文件上传 2. 文件上传 在我们完成…

从 React 到 Qwik:开启高效前端开发的新篇章

1. Qwik Qwik 是一个为构建高性能的 Web 应用程序而设计的前端 JavaScript 框架,它专注于提供即时启动性能,即使是在移动设备上。Qwik 的关键特性是它采用了称为“恢复性”的技术,该技术消除了传统前端框架中常见的 hydration 过程。 恢复性是一种序列化和恢复应用程序状态…

日常学习之:如何使用 dockerfile 将 vue 的单独前端项目通过 docker 的方式部署到 heroku上

文章目录 需求描述开始操作准备阶段&#xff1a;准备 server.js 文件并安装依赖&#xff0c;将 vue 项目包装成单独的服务器制作 server.js安装 server.js 需要的依赖 构建 Dockerfileheroku container 链接和部署其他细节 需求描述 你想用 vue 构建前端&#xff0c;用 django…

【设计模式】阿里终面:你觉得这个例子是策略模式吗?

什么是策略模式&#xff1f; 策略模式&#xff0c;举几个贴近生活的例子&#xff1a;当我们出行的时候&#xff0c;不同的出行方式就是不同的策略&#xff0c;例如走路、开车、骑自行车、坐飞机、坐邮轮等等&#xff0c;每一种出行方式都代表着不同的费用和时间&#xff1b;当…