Golang并发编程-协程goroutine的信道(channel)

文章目录

  • 前言
  • 一、信道的定义与使用
    • 信道的声明
    • 信道的使用
  • 二、信道的容量与长度
  • 三、缓冲信道与无缓冲信道
      • 缓冲信道
      • 无缓冲信道
  • 四、信道的初体验
    • 信道关闭的广播机制
  • 总结


前言

Goroutine的开发,当遇到生产者消费者场景的时候,离不开 channel(信道)的使用。
信道,就是一个管道,连接多个goroutine程序 ,它是一种队列式的数据结构,遵循先入先出的规则。
在这里插入图片描述


一、信道的定义与使用

信道的声明

信道声明的两种方式:

// 先声明再初始化
var 信道实例 chan 信道类型
信道实例 = make(chan 信道类型)

// 上面两句合并
信道实例 := make(chan 信道类型)

信道的使用

发送数据,接收数据

// 往信道中发送数据
pipline<- 200

// 从信道中取出数据,并赋值给mydata
mydata := <-pipline

信道用完了,可以对其进行关闭,避免有人一直在等待。但是你关闭信道后,接收方仍然可以从信道中取到数据,只是接收到的会永远是 0。

close(pipline)

当从信道中读取数据时,可以有多个返回值,其中第二个可以表示 信道是否被关闭,如果已经被关闭,ok 为 false,若还没被关闭,ok 为true。

x, ok := <-pipline

二、信道的容量与长度

一般创建信道都是使用 make 函数,make 函数接收两个参数

  • 第一个参数:必填,指定信道类型
  • 第二个参数:选填,不填默认为0,指定信道的容量(可缓存多少数据)

对于信道的容量,很重要,这里要多说几点:

  1. 当容量为0时,说明信道中不能存放数据,在发送数据时,必须要求立马有人接收,否则会报错。此时的信道称之为无缓冲信道。
  2. 当容量为1时,说明信道只能缓存一个数据,若信道中已有一个数据,此时再往里发送数据,会造成程序阻塞。 利用这点可以利用信道来做锁。
  3. 当容量大于1时,信道中可以存放多个数据,可以用于多个协程之间的通信管道,共享资源。

信道的容量,可以使用 cap 函数获取 ,而信道的长度,可以使用 len 长度获取。

package main

import "fmt"

func main() {
	pipline := make(chan int, 10)
	fmt.Printf("信道可缓冲 %d 个数据\n", cap(pipline))
	pipline <- 1
	pipline <- 1
	fmt.Printf("信道中当前有 %d 个数据\n", len(pipline))
}

输出结果

[root@work day01]# go run channel.go 
信道可缓冲 10 个数据
信道中当前有 2 个数据

三、缓冲信道与无缓冲信道

按照是否可缓冲数据可分为:缓冲信道无缓冲信道

缓冲信道

允许信道里存储一个或多个数据,这意味着,设置了缓冲区后,发送端和接收端可以处于异步的状态。

pipline := make(chan int, 10)

无缓冲信道

在信道里无法存储数据,这意味着,接收端必须先于发送端准备好,以确保你发送完数据后,有人立马接收数据,否则发送端就会造成阻塞,原因很简单,信道中无法存储数据。也就是说发送端和接收端是同步运行的。

pipline := make(chan int)

四、信道的初体验

信道的用途就是传递数据信息,下面以经典的生产者和消费者场景,实践下信道是使用。
下面的例子中,声明了一个容量为10的传递整型的信道。生产者生产的数据放入信道,消费者将从信道的数据打印。
这里,我们消费者有两种,其实是演示了信道遍历的两种常见的方式。
第一种是用for range方式
第二种是用for循环,断言判断信道是否关闭,关闭了就退出。

package main

import (
	"fmt"
	"sync"
)

// 生产者
func producer(mychan chan int, wg *sync.WaitGroup) {
	defer wg.Done()
	for i := 1; i <= 10; i++ {
		mychan <- i
		fmt.Printf("produce: %v\n", i)
		fmt.Printf("pipeline Length: %v \n", len(mychan))
	}

	// 记得 close 信道
	// 不然主函数中遍历完并不会结束,而是会阻塞。
	close(mychan)
}

//消费者 Range方式
func consumer_range(name string, mychan chan int, wg *sync.WaitGroup) {
	defer wg.Done()
	for k := range mychan {
		fmt.Printf("consumer_range %v: %v\n", name, k)
	}
}

//消费者 断言方式
func consumer_assert(name string, mychan chan int, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
	    // if data, ok := <-mychan; ok  ok为bool值,true表示正常接受,false表示通道关闭。
		if data, ok := <-mychan; ok {
			fmt.Printf("consumer_assert %v: %v\n", name, data)
		} else {
			break
		}
	}
}

func main() {

	var wg sync.WaitGroup
	pipline := make(chan int, 10)
	wg.Add(3)
	go producer(pipline, &wg)
	go consumer_range("ID1", pipline, &wg)
	go consumer_assert("ID2", pipline, &wg)
	wg.Wait()

}

输出的结果:

[root@work day01]# go run channel3.go 
produce: 1
pipeline Length: 0 
produce: 2
pipeline Length: 1 
produce: 3
pipeline Length: 2 
produce: 4
pipeline Length: 3 
produce: 5
pipeline Length: 4 
produce: 6
pipeline Length: 5 
produce: 7
pipeline Length: 6 
produce: 8
pipeline Length: 7 
produce: 9
pipeline Length: 8 
produce: 10
pipeline Length: 9 
consumer_assert ID2: 1
consumer_assert ID2: 2
consumer_assert ID2: 3
consumer_assert ID2: 4
consumer_assert ID2: 5
consumer_assert ID2: 6
consumer_assert ID2: 7
consumer_assert ID2: 8
consumer_assert ID2: 9
consumer_assert ID2: 10

信道关闭的广播机制

上面的案例中,有一点需要注意,我们的生产者函数中,在数据生产结束后,调用了close(mychan)方法,关闭了信道。如果不关闭程序还能正常执行吗?我们手动修改下代码,将代码改造如下,再次执行。

	// 记得 close 信道
	// 不然主函数中遍历完并不会结束,而是会阻塞。
	// close(mychan)

执行结果如下:

[root@work day01]# go run channel3.go 
produce: 1
pipeline Length: 0 
produce: 2
pipeline Length: 0 
produce: 3
pipeline Length: 1 
produce: 4
pipeline Length: 2 
produce: 5
pipeline Length: 3 
produce: 6
pipeline Length: 4 
produce: 7
pipeline Length: 5 
produce: 8
pipeline Length: 6 
produce: 9
pipeline Length: 7 
produce: 10
pipeline Length: 8 
consumer_assert ID2: 1
consumer_assert ID2: 3
consumer_assert ID2: 4
consumer_assert ID2: 5
consumer_assert ID2: 6
consumer_assert ID2: 7
consumer_assert ID2: 8
consumer_assert ID2: 9
consumer_assert ID2: 10
consumer_range ID1: 2
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc000010060?)
        /usr/local/go/src/runtime/sema.go:62 +0x27
sync.(*WaitGroup).Wait(0x0?)
        /usr/local/go/src/sync/waitgroup.go:116 +0x4b
main.main()
        /data/go_projects/third_packages/goroutine/day01/channel3.go:50 +0x157

goroutine 7 [chan receive]:
main.consumer_range({0x49bc4f, 0x3}, 0x0?, 0x0?)
        /data/go_projects/third_packages/goroutine/day01/channel3.go:25 +0x11f
created by main.main
        /data/go_projects/third_packages/goroutine/day01/channel3.go:48 +0xf9

goroutine 8 [chan receive]:
main.consumer_assert({0x49bc52, 0x3}, 0x0?, 0x0?)
        /data/go_projects/third_packages/goroutine/day01/channel3.go:34 +0x11d
created by main.main
        /data/go_projects/third_packages/goroutine/day01/channel3.go:49 +0x14d
exit status 2

通过上面的结果,我们看到消费者在消费完信道中的消息后,就panic退出了。
当程序一直在等待从信道里读取数据,而此时并没有人会往信道中写入数据。此时程序就会陷入死循环,造成死锁。
这里在现实生活中也有类似的例子,生产者是网红,每天摆摊,大量消费者蹲守在网红摊点前打卡,突然一天网红不出摊了,也没在微信和社交媒体更新动态,那消费者一天一直苦等。正确的做法是网红在社交媒体上发布下今天不出摊的通知,避免粉丝等待。

if data, ok := <-mychan; ok  ok为bool值,true表示正常接受,false表示通道关闭。

同理,我们上面的案例一样,当生产者不在生产的时候,应该关闭信道。因为信道关闭是有关播机制的,所有的channel接收者都会在chennel关闭时,立刻从阻塞等待中返回ok值为false。


总结

  1. 关闭一个未初始化的 channel 会产生 panic
  2. 重复关闭同一个 channel 会产生 panic
  3. 向一个已关闭的 channel 发送消息会产生 panic
  4. 从已关闭的 channel 读取消息不会产生 panic,且能读出 channel 中还未被读取的消息,若消息均已被读取,则会读取到该类型的零值
  5. 从已关闭的 channel 读取消息永远不会阻塞,并且会返回一个为 false 的值,用以判断该 channel 是否已关闭(x,ok := <- ch)
  6. 关闭 channel 会产生一个广播机制,所有向 channel 读取消息的 goroutine 都会收到消息
  7. 如果写端没有写数据,也没有关闭channel 。<-ch; 会阻塞

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Matplotlib包实现可视化①——折线图绘制

Matplotlib 是一个用于创建静态、动画、 和交互式可视化的第三方库&#xff0c;也是我们在借助Python进行数据可视化时经常使用到的库&#xff0c;具有较强的可视化能力。 为让大家有一个更为直观的认识&#xff0c;这里我随机从其官方网页中摘取了几张图片。 按照惯例&#x…

【安装笔记-20240523-Windows-安装测试 ShareX】

安装笔记-系列文章目录 安装笔记-20240523-Windows-安装测试 ShareX 文章目录 安装笔记-系列文章目录安装笔记-20240523-Windows-安装测试 ShareX 前言一、软件介绍名称&#xff1a;ShareX主页官方介绍 二、安装步骤测试版本&#xff1a;16.1.0下载链接功能界面 三、应用场景屏…

2024年【危险化学品经营单位安全管理人员】考试及危险化学品经营单位安全管理人员考试资料

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 危险化学品经营单位安全管理人员考试考前必练&#xff01;安全生产模拟考试一点通每个月更新危险化学品经营单位安全管理人员考试资料题目及答案&#xff01;多做几遍&#xff0c;其实通过危险化学品经营单位安全管理…

强化学习4:DQN 算法

看这篇文章之前&#xff0c;建议先了解一下&#xff1a;Q-Learning 算法。 1. 算法介绍 DQN 算法全称为 Deep Q-Network&#xff0c;即深度Q网络。它将 Q-Learning 与 Deep Learning 结合在了一起。 1.1 Q-Network Q-Learning 是使用 Q-table 才存储决策信息的&#xff0c;…

【Python001】python批量下载、插入与读取Oracle中图片数据(已更新)

1.熟悉、梳理、总结数据分析实战中的python、oracle研发知识体系 2.欢迎点赞、关注、批评、指正,互三走起来,小手动起来! 文章目录 1.背景说明2.环境搭建2.1 参考链接2.2 `oracle`查询测试代码3.数据请求与插入3.1 `Oracle`建表语句3.2 `Python`代码实现3.3 效果示例4.问题链…

嵌入式实时操作系统笔记3:FreeRTOS移植(STM32F407)_编写简单的FreeRTOS任务例程

上文讲到UC/OS III系统的移植&#xff0c;那篇文章是失败了的&#xff0c;网络上的资料真是层次不清&#xff0c;多有遗漏步骤&#xff0c;导致单片机连操作系统的初始化都卡在那&#xff0c;这次换个赛道&#xff0c;学FreeRTOS吧...... 今日任务如标题所示&#xff1a;FreeR…

2024年【熔化焊接与热切割】考试内容及熔化焊接与热切割考试报名

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 熔化焊接与热切割考试内容考前必练&#xff01;安全生产模拟考试一点通每个月更新熔化焊接与热切割考试报名题目及答案&#xff01;多做几遍&#xff0c;其实通过熔化焊接与热切割复审模拟考试很简单。 1、【单选题】…

Android 配置 Kapt 编译器插件

以 Android Studio 2023.3.1 最新版本为准。 步骤1:打开版本信息配置文件 找到libs.versions.toml文件。 这是打开后的样子&#xff1a; 步骤2&#xff1a;配置版本信息 你需要在[plugins]下面添加一条kapt的配置信息&#xff1a; 要添加的配置信息如下&#xff1a; jetbr…

Boxy SVG for Mac:打造精致矢量图形的得力助手

在矢量图形设计领域&#xff0c;Boxy SVG for Mac以其出色的性能和丰富的功能&#xff0c;成为了设计师们的得力助手。 Boxy SVG for Mac(矢量图编辑器) v4.32.0免激活版下载 Boxy SVG具备强大的编辑能力&#xff0c;支持节点编辑、路径绘制、颜色填充等多种操作&#xff0c;让…

小浪助手下载学浪视频的简单步骤

你是否想轻松下载学浪高清视频&#xff1f;快来尝试小浪助手&#xff0c;这是你不可错过的下载神器&#xff01;简单几步操作&#xff0c;即可轻松下载你所需的学浪视频&#xff0c;节省时间&#xff0c;提高效率&#xff01; 首先我已经打包好了小浪助手&#xff0c;有需要的…

毫米波雷达模块在智能家居安全系统中的关键技术分析

随着智能技术的不断发展&#xff0c;智能家居已经成为了现代生活的一部分。而智能家居安全系统作为智能家居的重要组成部分&#xff0c;其功能不仅仅是对家庭进行监控和报警&#xff0c;更是通过各种感知技术对家庭安全进行全方位的保障。在智能家居安全系统中&#xff0c;毫米…

二叉树的遍历(前序遍历,中序遍历,后序,层序遍历)和一些简单操作(由浅入深绝对能看懂)

欢迎大佬们的关顾能给个赞就更好啦QWQ 目录 二叉树的逻辑结构与物理结构 一.二叉树的遍历 &#xff08;1&#xff09;二叉树的前序遍历 &#xff08;2&#xff09;二叉树的中序遍历 &#xff08;3&#xff09;二叉树的后序遍历 &#xff08;4&#xff09;二叉树的层序遍历…

Python 渗透测试:电子邮件 || Redis || FTP || SSH || MySQL 集成爆破工具.

集成爆破工具. 集合爆破里面包含了&#xff1a;电子邮件爆破工具&#xff0c;Redis爆破工具&#xff0c;FTP爆破工具&#xff0c;SSH爆破工具&#xff0c;MySQL爆破工具。 目录&#xff1a; 集合爆破工具. 电子邮件 爆破工具&#xff1a; Redis 爆破工具&#xff1a; FTP …

sqlserver 创建表,列及表,列描述

-- 创建表 CREATE TABLE Employees (EmployeeID INT PRIMARY KEY,EmployeeName NVARCHAR(100),EmployeeEmail NVARCHAR(100) );-- 为表添加描述 EXEC sp_addextendedproperty name NMS_Description, value N员工信息表, level0type NSchema, level0name dbo, level1type N…

数字图像处理冈塞雷斯第四版课后习题答案【英文原版】

第二章 第三章 . 第四章 傅里叶变换是一个线性过程&#xff0c;而计算梯度的平方根和平方根则是非线性运算。傅里叶变换可以用来计算微分的差值(如问题4.50)&#xff0c;但必须在空间域中直接计算平方和平方根值。 (a)实际上&#xff0c;由于高通操作&#xff0c;环有一个暗中心…

基于MetaGPT构建LLM多智能体

前言 你好&#xff0c;我是GISer Liu&#xff0c;在上一篇文章中&#xff0c;我们用了两万多字详细拆解了单个Agent的组成&#xff0c;并通过Github Trending订阅智能体理解MetaGPT框架的订阅模块如何解决应用问题&#xff0c;但是对于复杂&#xff0c;并行的任务&#xff0c;单…

010-Linux磁盘介绍

文章目录 1、名词 2、类型 3、尺寸 4、接口/协议/总线 5、命名 6、分区方式 MBR分区 GPT分区 1、名词 磁盘是计算机主要的存储介质&#xff0c;可以存储大量的二进制数据&#xff0c;并且断电后也能保持数据不丢失。早期计算机使用的磁盘是软磁盘&#xff08;Floppy D…

VBA技术资料MF157:创建每个标题的目录

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

【计算机毕业设计】基于SSM+Vue的校园美食交流系统【源码+lw+部署文档】

目录 前 言 第1章 概述 1.1 研究背景 1.2 研究目的 1.3 研究内容 第二章 开发技术介绍 2.1 Java技术 2.2 Mysql数据库 2.3 B/S结构 2.4 SSM框架 第三章 系统分析 3.1 可行性分析 3.1.1 技术可行性 3.1.2 经济可行性 3.1.3 操作可行性 3.2 系统性能分析 3.3 系…

(避坑)SpringSecurity关于使用.antMatchers放行接口不生效问题

问题 在使用SpringSecurity的时候发现放行指定接口一直没有生效&#xff0c;使用"/**"就可以生效的问题 关于securityConfig的配置代码 Beanprotected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.csrf().disable() // 关闭csrf防护…