【Go】Channel底层实现 ②

news2024/11/23 22:28:36

文章目录

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

channel底层实现

// channel 类型定义
type hchan struct {
    // channel 中的元素数量, len
    qcount   uint           // total data in the queue
    
    // channel 的大小, cap
    dataqsiz 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 index
    recvx    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          *g
    selectdone *uint32 // CAS to 1 to win select race (may point to stack)
    next       *sudog
    prev       *sudog
    elem       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 int64
    releasetime int64
    ticket      uint32
    parent      *sudog // semaRoot binary tree
    waitlink    *sudog // g.waiting list or semaRoot
    waittail    *sudog // semaRoot
    c           *hchan // channel
}

在这里插入图片描述

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

type waitq struct {
	first *sudog
	last  *sudog
}

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

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

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

package main

import (
	"fmt"
	"time"
)

func main() {

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

Channel是异步进行的。

channel发送、接收数据

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

// G2
func worker() { 
	for { 
		task := <-taskCh
		process(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.coloradmin.cn/o/1415851.html

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

相关文章

1.26学习总结

连通性判断 DFS连通性判断步骤&#xff1a; 1.从图上任意一点u开始遍历&#xff0c;标记u已经走过 2.递归u的所有符合连通条件的邻居点 3.递归结束&#xff0c;找到了的所有与u的连通点&#xff0c;就是一个连通块 4.然后重复这个步骤找到所有的连通块 BFS连通性判断步骤…

opencv学习二值分析

内容来源于《opencv4应用开发入门、进阶与工程化实践》 二值分析&#xff1a; 常见的二值化方法&#xff1a; 基于全局阈值&#xff08;threshold&#xff09;得到的二值图像&#xff1b;基于自适应阈值&#xff08;adaptiveThreshold&#xff09;得到的二值图像&#xff1…

RPC教程 7.服务发现与注册中心

0.前言 这一节的内容只能解决只有一个服务的情况。要是有多个服务(即是多个结构体&#xff09;这种就解决不了&#xff0c;也即是没有服务ip地址和服务实例的映射关系。 1.为什么需要注册中心 在上一节中&#xff0c;客户端想要找到服务实例的ip,需要硬编码把ip写到代码中。…

永磁直驱式风力发电虚拟同步机仿真模型Matlab/Simulink模型

很久没有分享虚拟同步机控制相关的方向了&#xff0c;主要是因为硕士之后&#xff0c;也就没再继续深入研究这个课题了&#xff0c;更多的是在电科院的项目里会接触。这个课题方向其实作为硕士毕业课题还是够用的&#xff0c;相对来说也是比较容易毕业的&#xff0c;因为涉及的…

x-cmd pkg | go - Google 开发的开源编程语言

目录 简介首次用户技术特点竞品分析编译型语言解释型语言JavaWebAssebmly 进一步阅读 简介 Go 语言&#xff08;或 Golang&#xff09;是 Google 开发的开源编程语言&#xff0c;诞生于 2006 年。其设计目标是“兼具 Python 等动态语言的开发速度和 C/C 等编译型语言的性能与安…

Nodejs前端学习Day3_准备工作

妈的&#xff0c;这几天真tm冷&#xff0c;前天上午还下了一整天的雪&#xff0c;大雪 文章目录 前言一、Node.js简介1.1何为1.2有什么 二、Node.js可以做什么三、学习路线四、下载nodejs4.1小坑记录4.2LTS和Current版本的不同 五、什么是终端六、在nodejs中执行js代码七、powe…

Python 中的 strip 函数用法,你真的学会了吗?

Python 提供了大量内置函数&#xff0c;使编程变得更加简单。strip 就是这样一个函数。在本文中&#xff0c;我们将探讨 strip 是什么、为什么有用以及如何有效地使用它。 什么是 strip strip 是一种内置方法&#xff0c;用于删除字符串中的前导字符和尾随字符。这些字符可以…

MYSQL库和表的操作(修改字符集和校验规则,备份和恢复数据库及库和表的增删改查)

文章目录 一、MSYQL库的操作1.连接MYSQL2.查看当前数据库3.创建数据库4.字符集和校验规则5.修改数据库6.删除数据库7.备份和恢复8.查看连接 二、表的操作1.创建表2.查看表结构3.修改表4.删除表 一、MSYQL库的操作 1.连接MYSQL 我们使用下面的语句来连接MSYQL&#xff1a; my…

太阳能 LED 恒流电源 升降压原理图 AP9193 大功率升压恒流IC

特别 宽输入电压范围&#xff1a;3.6V~100V 高效率&#xff1a;可高达 95% 工作频率&#xff1a;1MHz CS 限流保护电压&#xff1a;250mV FB 电流采样电压&#xff1a;250mV 芯片供电欠压保护&#xff1a;2.5V 关断时间可调 外置频率补偿脚 应用领域 LED 灯杯 电池供…

docker容器运维命令

文章目录 docker psdocker execdocker inspectdocker topdocker attachdocker waitdocker exportdocker importdocker portdocker cpdocker diffdocker renamedocker statsdocker update总结 docker ps 列出容器。 docker ps [OPTIONS]OPTIONS说明&#xff1a; -a :显示所有的…

FPGA 通过 UDP 以太网传输 JPEG 压缩图片

FPGA 通过 UDP 以太网传输 JPEG 压缩图片 简介 在 FPGA 上实现了 JPEG 压缩和 UDP 以太网传输。从摄像机的输入中获取单个灰度帧&#xff0c;使用 JPEG 标准对其进行压缩&#xff0c;然后通过UDP以太网将其传输到另一个设备&#xff08;例如计算机&#xff09;&#xff0c;所有…

计算方法实验1:熟悉MATLAB 环境

一、问题描述 熟悉MATLAB 环境。 二、实验目的 了解Matlab 的主要功能&#xff0c;熟悉Matlab 命令窗口及文件管理&#xff0c;Matlab 帮助系统。掌握命令行的输入及编辑&#xff0c;用户目录及搜索路径的配置。了解Matlab 数据的特点&#xff0c;熟悉Matlab 变量的命名规则&a…

如何独立思考?这里有一份全指南

知乎上有一个问题&#xff0c;叫做&#xff1a;为什么我们要独立思考&#xff1f; 排名第一的回答&#xff0c;是凤凰前主笔王路写的&#xff0c;很有意思。他说&#xff1a; 因为别人告诉我们要独立思考。 这个回答非常妙&#xff0c;也非常反讽&#xff0c;有一种「第22条军规…

32GPIO输入&按键控制LED&光敏控制蜂鸣器

一.硬件 光线越强&#xff0c;光敏电阻的阻值越小 温度越高&#xff0c;热敏电阻的阻值就越小 红外光线越强&#xff0c;红外接收管的阻值就越小 类比&#xff1a;电阻阻值越小&#xff0c;上拉或下拉就越强 &#xff08;弹簧的拉力就越强&#xff09; 在上下的电阻分压下&a…

Android Studio 提示Use app:drawableStartCompat instead of android:drawableStart

每次提交代码时&#xff0c;AS这个老妈子总爱唠叨一堆warning&#xff0c;这些Warning都在讲什么&#xff1f; 1.Use app:drawableStartCompat instead of android:drawableStart 在Android开发中&#xff0c;android:drawableStart和app:drawableStartCompat是两个用于设置…

Linux下的进程操作

进程概念 ps -elf&#xff1a;查看操作系统的所有进程&#xff08;Linux命令&#xff09; ctrl z&#xff1a;把进程切换到后台 crtl c&#xff1a;结束进程 fg&#xff1a;把进程切换到前台 获取进程进程号和父进程号 函数原型&#xff1a; pid_t getpid(void); //pid_t…

2.精确度-机器学习模型性能常用的评估指标

一.精确度的定义 精确度&#xff1a;机器学习领域中一项至关重要的评价指标&#xff0c;其专注于评估模型对正样本的预测准确性。 相对于准确率而言&#xff0c;精确度更为细致&#xff0c;它关注的是模型在将实例预测为正样本的情况下&#xff0c;实际为正样本的比例。换句话…

2024 1.20~1.26周报

一、上周工作 了解注意力机制&#xff0c;开始论文的初步阅读 二、本周计划 简单了解transform架构&#xff0c;继续研读论文U-MixFormer: UNet-like Transformer with Mix-Attention for Efficient Semantic Segmentation。 三、完成情况——论文研读 标题&#xff1a;U-Mi…

Android双指缩放ScaleGestureDetector检测放大因子大图移动到双指中心点ImageView区域中心,Kotlin(2)

Android双指缩放ScaleGestureDetector检测放大因子大图移动到双指中心点ImageView区域中心&#xff0c;Kotlin&#xff08;2&#xff09; 在 Android ScaleGestureDetector检测双指缩放Bitmap基于Matrix动画移动到双指捏合中心点ImageView区域中心&#xff0c;Kotlin-CSDN博客 …

12.Elasticsearch应用(十二)

Elasticsearch应用&#xff08;十二&#xff09; 1.单机ES面临的问题 海量数据存储问题单点故障问题 2.ES集群如何解决上面的问题 海量数据存储解决问题&#xff1a; 将索引库从逻辑上拆分为N个分片&#xff08;Shard&#xff09;&#xff0c;存储到多个节点单点故障问题&a…