Go —— channel (二)

news2024/12/23 5:06:23

一个空的 channel 会产生哪些问题

读写nil管道均会阻塞触发死锁。关闭的管道仍然可以读取数据,向关闭的管道写数据会触发panic。

问:如果有多个协程同时读取一个channel,channel会如何选择消费者

channel 会按照维护的 recvq 等待读消息的协程队列按照FIFO的顺序选择消费者

我们先来看一下 channel 源码

type hchan struct {
	qcount   uint           // 当前队列中剩余元素个数
	dataqsiz uint           // 环形队列长度,即可以存放的元素个数
	buf      unsafe.Pointer // 环形队列指针 缓冲区
	elemsize uint16			// 每个元素的大小
	closed   uint32			// 标识关闭状态
	elemtype *_type // 元素类型
	sendx    uint   // 队列下标,指示元素写入时存放到队列中的位置
	recvx    uint   // 队列下标,指示下一个被读取元素在队列中的位置
	recvq    waitq  // 等待读消息的协程队列
	sendq    waitq  // 等待写消息的协程队列
	lock mutex		// 互斥锁,chan不允许并发读写
}

向管道写数据

向一个管道中写数据的简单过程如下

  • 如果缓冲区中有空余位置,则将数据写入缓冲区,结束发送过程
  • 如果缓冲区中没有空余位置,则将当前协程加入sendq队列,进入休眠并等待被读协程唤醒

简单流程如下图所示

在这里插入图片描述

向管道读数据

channel 会维护一个等待读消息的协程队列 recvq,当一个协程读取消息时的简单过程如下:

  • 如果缓冲区中有数据,则从缓冲区中取出数据,结束读取过程
  • 如果缓冲区中没有数据,则将当前协程加入 recvq 队列,进入休眠并等待被写协程唤醒

如果 sendq 不为空,且没有缓冲区,则会从 sendq队列的第一个协程中获取数据

简单流程如下图所示:

在这里插入图片描述

编写一个程序,测试一下

func main() {
	c := make(chan int)
	wg := sync.WaitGroup{}
	wg.Add(100)
	go func() {				// G1
		for {
			a := <-c
			fmt.Println("1", a)
			wg.Done()
		}
	}()
	go func() {				// G2
		for {
			a := <-c
			fmt.Println("2", a)
			wg.Done()
		}
	}()
	go func() {				// G3
		for {
			a := <-c
			fmt.Println("3", a)
			wg.Done()
		}
	}()
	go func() {				// G4
		for {
			a := <-c
			fmt.Println("4", a)
			wg.Done()
		}
	}()
    time.Sleep(1 * time.Second)		// 等待四个协程排好队
	for i := 0; i < 100; i++ {
		c <- i
	}
	wg.Wait()
}

按照上面协程读取消息的过程会发生什么呢?

  1. 当c还未被写入消息时, G1~G4 以FIFO的原则排好队,比如现在的recvq的顺序为 G1、G2、G3、G4
  2. 当主协程发送消息时,无缓冲区,直接从recvq 队列中取出头部G ,随后再添加到队尾
  3. c中的数据按照G1、G2、G3、G4的顺序依次被读取

那实际执行结果是怎样的呢

在我多次测试后发现前四个数会被每个协程消费一次,随后会出现大片数据被同一协程消费的情况

3 2					// 前四次
3 4
3 5
3 6
3 7
3 8
3 9
3 10
3 11
3 12
3 13
3 14
3 15
3 16
3 17
3 18
3 19
3 20
3 21
3 22
3 23
3 24
3 25
3 26
3 27
3 28
3 29
3 30
3 31
3 32
3 33
3 34
3 35
3 36
3 37
3 38
3 39
3 40
3 41
3 42
3 43
3 44
3 45
3 46
3 47
3 48
3 49
3 50
3 51
3 52
3 53
3 54
3 55
3 56
3 57
3 58
3 59
3 60
3 61
3 62
3 63
3 64
3 65
3 66
3 67
3 68
3 69
3 70
3 71
3 72
3 73
3 74
3 75
3 76
3 77
3 78
3 79
3 80
3 81
3 82
3 83
3 84
3 85
3 86
3 87
3 88
3 89
3 90
3 91
3 92
3 93
3 94
3 95
3 96
3 97
3 98
3 99
2 1					// 前四次
1 0					// 前四次
4 3					// 前四次

Process finished with the exit code 0

出现这种情况可能与GMP调度模型有关系,当我们继续增大数据量后(比如增加到10000),会发现每个协程读取chan的次数其实差不多。

当我们向管道写数据时添加一个间隔时间

func main() {
	c := make(chan int)
	wg := sync.WaitGroup{}
	wg.Add(100)
	go func() {				// G1
		for {
			a := <-c
			fmt.Println("1", a)
			wg.Done()
		}
	}()
	go func() {				// G2
		for {
			a := <-c
			fmt.Println("2", a)
			wg.Done()
		}
	}()
	go func() {				// G3
		for {
			a := <-c
			fmt.Println("3", a)
			wg.Done()
		}
	}()
	go func() {				// G4
		for {
			a := <-c
			fmt.Println("4", a)
			wg.Done()
		}
	}()
    time.Sleep(1 * time.Second)		// 等待四个协程排好队
	for i := 0; i < 100; i++ {
       	time.Sleep(1 * time.Millisecond)	// 每隔1ms 发送一次
		c <- i
	}
	wg.Wait()
}

执行程序,会发现按照某种固定的顺序输出,这时完全符合上图的读的过程的

1 0
2 1
3 2
4 3
1 4
2 5
3 6
4 7
...

ps:如果有哪位老哥知道为什么会出现一个协程连续输出的情况,欢迎在评论区讨论

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

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

相关文章

结构体及联合体大小计算

结构体大小计算 结构体大小的计算的依据是结构体内存对齐 对齐规则&#xff1a; 1.结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处 2.其他成员变量要对齐到某个数字&#xff08;对齐数&#xff09;的整数倍的地址处。 &#xff08;对齐数编译器默认的一个对齐…

云数据库价格一瞥(华为云、百度智能云、腾讯云、阿里云)

最近&#xff0c;大家似乎和价格“磕”上了。本文仅考虑主流产品&#xff08; RDS MySQL、Redis &#xff09;的部分主流规格&#xff0c;对各家厂商的价格做一个对比&#xff0c;供参考。 TL;DR&#xff1a; 总体来看&#xff0c;各家云厂商价格趋于持平&#xff0c;部分主流商…

Android音视频的基础

视频是什么&#xff1f; 视频就是由一系列图片构成的。 视频帧 帧&#xff0c;是视频的一个基本概念&#xff0c;表示一张画面&#xff0c;如上面的翻页动画书中的一页&#xff0c;就是一帧。一个视频就是由许许多多帧组成的。 帧率 帧率&#xff0c;即单位时间内帧的数量&a…

VS Code开发插件使用 pnpm 打包异常的解决姿势

前言 刚刚准备发一个插件&#xff0c;发现用 pnpm 打出一个本地插件包直接扑街了。 这里只聚焦错误问题的解决&#xff0c;不是发插件的教程。。 聊点背景信息&#xff0c;vscode 的插件命令行的是 vsce 这个模块提供的 cli 能力去做的 环境 pnpm : 8.x 错误截图 本地打…

image with CV

""" 视觉&#xff1a;基本API应用&#xff08;OPENCV&#xff09; """ import cv2 import numpy as np"""图像读取方式3. 1.cv2.imread(filename or path, flags)flags0:灰度图像&#xff1b;flags1表示RGB图像&#xff1b;fl…

Apache Incubator Answer 本地开发部署

文章目录 简介Github文档插件部署 Answer开发环境编译项目初始化项目运行项目 简介 一款适合任何团队的问答平台软件。 Apache Incubator Answer是一个开源项目&#xff0c;它是一个用于构建和部署问答系统的框架。该项目是Apache软件基金会的孵化器项目&#xff0c;提供一个…

iOS 开发中上传 IPA 文件的方法(无需 Mac 电脑)

引言 在 iOS 开发中&#xff0c;将 IPA 文件上传到苹果开发者中心是一个重要的步骤。通常情况下&#xff0c;我们需要使用 Mac 电脑上的 Xcode 或 Application Loader 工具来完成这个任务。然而&#xff0c;如果你没有 Mac 电脑&#xff0c;也没有关系&#xff0c;本文将介绍一…

【图论】Leetcode 994. 腐烂的橘子【中等】

腐烂的橘子 在给定的 m x n 网格 grid 中&#xff0c;每个单元格可以有以下三个值之一&#xff1a; 值 0 代表空单元格&#xff1b;值 1 代表新鲜橘子&#xff1b;值 2 代表腐烂的橘子。 每分钟&#xff0c;腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。 返回 直到单…

运营商名称 是如何显示到 手机通知栏上的?

在我们日常使用手机的过程中&#xff0c;经常会在通知栏或设置菜单中看到特定的运营商名称&#xff0c;例如"中国移动"、"中国联通"或"中国电信"等。 那么&#xff0c;这些运营商的名称是如何出现在我们手机上的呢&#xff1f;手机又是如何区分不…

15. 【Android教程】按钮 Button/ImageButton

在前面两章我们讲了 TextView&#xff0c;它是一个纯输出的控件&#xff1b;而 EditText 在 TextView 基础之上加入了简单的输入功能&#xff1b;今天要讲的 Button 是一个和用户互动感很强的控件&#xff0c;从今往后不再是单纯的文本展示&#xff0c;我们可以通过 TextView、…

汽车4S行业的信息化特点与BI建设挑战

汽车行业也是一个非常大的行业&#xff0c;上下游非常广&#xff0c;像主机厂&#xff0c;上游的零配件&#xff0c;下游的汽车流通&#xff0c;汽车流通之后的汽车后市场&#xff0c;整个链条比较长。今天主要讲的是汽车流通&#xff0c;汽车4S集团。一个汽车4S集团下面授权代…

微信小程序使用iconfont

进入iconfont&#xff0c;添加至项目 进入项目&#xff0c;点击生成代码&#xff0c;或更新代码 点击打开样式 复制内容到小程序的style文件夹下 最后引入到app.wxss

C++中的string设计成模板的原因

查看string的文档可以发现&#xff0c;string被设计成模板&#xff0c;为什么这样设计呢&#xff1f; string文档链接&#xff1a;<string> - C Reference (cplusplus.com) 随着计算机的发展&#xff0c;出现了很多编码&#xff08;用于管理字符&#xff09;&#xff0c…

https的配置和使用(以腾讯云为例)

1、注册域名 2、获取证书 3、下载证书 下载下来的证书所有格式 4、在服务器上下载nginx并配置 nginx的配置文件 如下 server {listen 80;listen 443 ssl;server_name delegate.letspiu.net.cn;ssl on; #开启ssl#指定证书位置ssl_certificate /etc/ss…

集成 LlamaIndex 和 Qdrant 相似性搜索以进行患者记录检索

介绍 由于医疗技术、数字健康记录(EHR)和可穿戴健康设备的进步,医疗领域目前正在经历数据的显着激增。有效管理和分析这些复杂多样的数据的能力对于提供定制医疗保健、推进医学研究和改善患者健康结果至关重要。矢量数据库是专门为高效处理和存储多维数据而定制的,作为一系…

Golang | Leetcode Golang题解之第17题电话号码的字母组合

题目&#xff1a; 题解&#xff1a; var phoneMap map[string]string map[string]string{"2": "abc","3": "def","4": "ghi","5": "jkl","6": "mno","7": &…

爬虫 BeautifulSoup模块

爬虫 BeautifulSoup模块 【一】介绍 【1】说明 BeautifulSoup库是python的一个第三方库&#xff0c;主要用于处理HTML和XML文档他提供了一些简单的、python式的函数来解析、导航、搜索以及修改分析树&#xff0c;使得从网页抓取的数据变得简单高效BeautifulSoup自动将输入文…

Debian安装1panel管理面板教程-最新

1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。 1Panel面板是一个强大的服务器管理工具&#xff0c;它通过提供一站式管理、易于使用的界面、高度的可定制性、安全可靠的性能、强大的扩展性以及活跃的社区支持&#xff0c;为用户提供了一个高效、便捷的管理解决方案…

「2024」React 状态管理入门

概念 简单来说&#xff0c;状态指的是某一时刻应用中的数据或界面的呈现。这些数据可能包括用户填写表单的信息、应用内的用户偏好设置、应用的页面/路由状态、或者任何其他可能改变UI的信息。 状态管理是前端开发中处理用户界面(UI)状态的过程&#xff0c;在复杂应用中尤其重…

如何让MacOS「终端」走代理

在 MacOS 操作系统中&#xff0c;默认情况下&#xff0c;终端命令行不会通过代理进行网络连接。这导致在应用软件研发过程中&#xff0c;许多需要通过命令行下载安装的软件或依赖包无法成功安装。经常出现Failed to connect to xxx port 443 after 75329 ms: Couldnt connect t…