Go语言 Channel

基本介绍

Channel 是 Go 中的一个核心类型,可以把它看成一个管道。
利用通道我们可以在多个 goroutine 之间传递数据。
如果说 Goroutine 是 Go 程序并发的执行体,Channel 就是它们之间的连接。
Channel 是可以让一个 Goroutine 发送特定值到另一个 Goroutine 的通信机制。
Channel 像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。

基本使用

声明

  1. 声明 channel: var 变量 chan 元素类型
  2. 声明的通道后需要使用 make 函数初始化之后才能使用
	var ch1 chan int   // 声明一个传递整型的通道var ch2 chan bool  // 声明一个传递布尔型的通道var ch3 chan string // 声明一个传递字符串的通道     
	ch1 := make(chan int) // 直接初始化一个无缓冲通道ch2 := make(chan int,2) // 初始化一个带缓冲的通道

操作

  1. 通道有发送(send)、接收(receive)和关闭(close)三种操作。
  2. 发送和接收都使用 <- 符号。

发送

  1. 使用 <- 符号放到通道变量右边,就可以把值发送到创建的通道中,看起来就像值流向通道中。
	ch1 := make(chan int ,1)ch1 <-1

接收

  1. 使用 <- 符号放到通道变量左边,就可以从通道中接收一个值,看起来就像通道中流出一个值到变量中。
	ch1 := make(chan int ,1)ch1 <-1result := <- ch1fmt.Println(result) // 1

关闭

使用 close 函数可以把通道关闭

	close(ch1)

关闭通道特点

虽然关闭通道看起来很简单,但是在实际场景中需要特别注意通道的关闭,如果不能正确通道,会引起一些意想不到的错误。
关闭后的通道有以下特点:

  1. 对一个关闭的通道再发送值就会导致 panic (demo1)。
  2. 关闭一个已经关闭的通道会导致 panic (demo2)
  3. 对一个关闭的通道进行接收会一直获取值直到通道为空 (demo3)。
  4. 对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值 (demo4)。

demo1

	func demo1(){ch1 := make(chan int ,1)close(ch1)ch1 <- 1  // panic: send on closed channel}

demo2

	func demo2(){ch1 := make(chan int ,1)close(ch1)close(ch1) // panic: close of closed channel}

demo3

	func demo3(){ch1 := make(chan int ,3)for i:=0;i<3;i++{ch1 <- i}close(ch1)for v := range ch1{fmt.Println(v)}}
  1. 如果这里不关闭 ch1 这个通道,下面的 for range 会一直去遍历 通道
  2. 没有值的话就会造成阻塞,在主协程里造成阻塞的话,就会造成死锁报以下致命错误
  3. 所以在使用 for range 去遍历通道的时候要注意关闭,或者在子协程里去这样使用。
	fatal error: all goroutines are asleep - deadlock!

demo4

	func demo4(){ch1 := make(chan int ,1)ch1 <-1close(ch1)fmt.Println(<-ch1)  //1fmt.Println(<-ch1) // 0result , ok := <- ch1fmt.Println(result) // 0fmt.Println(ok) //false}
  1. 通道关闭了是可以继续接收值的
  2. 如果有值则接收值,没有值则返回通道类型的零值
  3. 如果想知道通道是否关闭,可以接收第二个值,通过第二个值来判断通道是否关闭

无缓冲通道

通道有两种,无缓冲通道和带缓冲通道,先看下无缓冲通道。
无缓冲通道,发送者和接受者都要存在,有一方不存在会导致阻塞。
所以说无缓冲的通道又被称为阻塞的通道。

	ch := make(chan int)ch <- 10fmt.Println("发送成功")

以上代码会报错

	fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan send]:

为什么会报错呢?
我们使用 ch := make(chan int)创建的是无缓冲的通道。
无缓冲的通道只有在有接收方接收值的时候才能发送值,但是在一个协程里执行到发送方或者接收方的时候就会阻塞,所以需要在两个协程间接收和发送。

代码调整

	func demo5(){ch := make(chan int)go func() {result := <- chfmt.Println("接收成功",result)}()ch <- 10fmt.Println("发送成功")}
	func demo6(){ch := make(chan int)go func() {ch <- 10fmt.Println("发送成功")}()result := <- chfmt.Println("接收成功",result)}

上面两种形式都可以,只要不要造成主协程阻塞就可以,主协程如果阻塞了就会报致命错误死锁。

缓冲通道

解决无缓冲通道(阻塞)死锁的问题,就是使用有缓冲的通道。通过缓存的使用,可以尽量避免阻塞,提高应用的性能。
带缓冲的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。
我们使用 make 函数在初始化的时候为其指定通道的容量(缓冲大小):
只要通道的容量大于零,那么该通道就是有缓冲的通道,通道的容量表示通道中能存放元素的数量。
我们可以使用内置的 len 函数获取通道内元素的数量,使用 cap 函数获取通道的容量。

	func demo7(){ch := make(chan int ,1)  // 创建一个容量为 1 的有缓冲区的通道ch <- 10fmt.Println("发送成功")result := <- chfmt.Println("接收成功",result)}

这个例子中,创建了带1个缓冲的通道,所以往里面写值的时候没有造成阻塞。

	ch := make(chan int ,1)  // 创建一个容量为 1 的有缓冲区的通道ch <- 10 ch <- 10fmt.Println("发送成功")

这个例子中,第一次写值的时候可以,第二次写值的时候就会阻塞然后报死锁错误。

扇入和扇出

扇出/扇入模式是并发编程中常用的设计模式,特别是在 Go 语言中。它包括两个阶段:

  1. 扇出阶段,在这个阶段,单个 goroutine 将任务广播给多个工作 goroutine;
  2. 扇入阶段,在这个阶段,这些工作 goroutine 的结果被聚合到一个单一的通道中。

FAN - OUT 扇出

	func demo9() {taskChan := make(chan int, 10)quitChan := make(chan int,3)go func() {for i := 1; i <= 10; i++ {taskChan <- i}close(taskChan)}()go func() {for v := range taskChan{fmt.Println("work 1 处理任务:",v)}quitChan <-1}()go func() {for v := range taskChan{fmt.Println("work 2 处理任务:",v)}quitChan <-1}()go func() {for v := range taskChan{fmt.Println("work 3 处理任务:",v)}quitChan <-1}()<- quitChan<- quitChan<- quitChanfmt.Println("over")}

输出结果

	work 3 处理任务: 2work 3 处理任务: 4work 3 处理任务: 5work 3 处理任务: 6work 3 处理任务: 7work 3 处理任务: 8work 3 处理任务: 9work 3 处理任务: 10work 1 处理任务: 1work 2 处理任务: 3over

FAN - IN 扇入

func demo10(){taskChan := make(chan int, 10)quitChan := make(chan int,3)overChan := make(chan int)go func() {for i := 1; i <= 5; i++ {taskChan <- i}quitChan <-1}()go func() {for i := 5; i <= 10; i++ {taskChan <- i}quitChan <-1}()go func() {for v := range taskChan{fmt.Println("work  处理任务:",v)}fmt.Println("任务全部处理了")overChan <- 1}()<-quitChan<-quitChanclose(taskChan)<-overChanfmt.Println("over")
}
	work  处理任务: 5work  处理任务: 6work  处理任务: 7work  处理任务: 8work  处理任务: 9work  处理任务: 10work  处理任务: 1work  处理任务: 2work  处理任务: 3work  处理任务: 4work  处理任务: 5任务全部处理了over

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

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

相关文章

动态活码二维码怎么制作?在线二维码生成器的使用技巧

二维码是如何生成的呢&#xff1f;现在二维码与我们的工作和生活息息相关&#xff0c;越来越多的场景都会有不同类型的二维码&#xff0c;比如常见的有视频、图片、文件、问卷、文本等等类型的内容。面对不同用途需求来制作二维码来为其他人提供内容展示&#xff0c;提升用户获…

Linux的DNS域名解析服务

目录 1.DNS 1.1定义 1.2作用/功能 1.3域名结构 1.4两种查询方式 1.5DNS域名解析工作原理 1.6DNS系统类型 2.正向解析实验​ 2.1安装bind服务&#xff0c;查看配置文件 2.2配置文件配置及文件内容说明 3.反向解析实验 4.配置主从DNS服务器 1.DNS 1.1定义 DNS域名系…

centos7搭建maven私服nexus

1.nexus Nexus Repository Manager&#xff08;通常简称 Nexus 或 Nexus RM&#xff09;是由Sonatype公司开发的一款开源的、强大的软件仓库管理工具&#xff0c;主要用于企业级的二进制组件&#xff08;如Java库、Node.js模块、Python包等&#xff09;存储、管理和分发。 官方…

使用 GORM 自定义类型:解决问题与技巧分享

引言 在使用 Go 语言的 ORM 库 GORM 进行数据模型操作时&#xff0c;开发者经常会遇到如何处理自定义数据类型的问题。本文将基于一个具体的例子 —— SliceString 类型&#xff0c;分享如何在 GORM 中处理自定义类型的字段&#xff0c;并通过设置 GORM 标签来指定类型&#x…

库存数据可视化分析按这个做,赚大了!

今天我们来看一张库存数据可视化分析驾驶舱&#xff0c;全面了解库存资金占用情况&#xff0c;物料周转情况&#xff0c;库存趋势情况、以及占库存金额最高的商品有哪些等。 为更好地实现以上效果&#xff0c;并且增强报表的可读性、易读性&#xff0c;我们采用了按分析场景选…

果断收藏|项目中有哪些风险是难以避免的?

我们在做项目的时候&#xff0c;总能遇到各种各样的风险。 为了尽可能规避风险或者减轻风险对项目造成的影响&#xff0c;我们会通过一些特定的方法对风险进行管理。 所谓风险管理&#xff0c;就是通过对风险的认识、衡量和分析&#xff0c;选择最有效的方式&#xff0c;主动…

TypeError: Unknown file extension “.ts“

报错 ts-node effect.ts ts-node 是一个 Node.js 的 TypeScript 执行器&#xff0c;它可以实时将 TypeScript 代码编译成 JavaScript 并执行。你可以通过 npm install -g ts-node 安装 ts-node&#xff08;全局安装&#xff09;&#xff0c;然后使用 ts-node 命令来执行 .ts 文…

数据被“锁”?别急,教你如何解锁被“rmallox”勒索病毒加密的文件

在当今数字化时代&#xff0c;网络安全问题日益凸显。其中&#xff0c;勒索病毒成为了一种常见的网络威胁&#xff0c;而rmallox勒索病毒则是其中的一种典型代表。本文将从病毒特性、传播途径、防范策略、紧急措施以及从中得到的社会启示等多个角度&#xff0c;深入探讨rmallox…

什么是云手机?云手机有什么用?

过去&#xff0c;我们手中的手机是我们生活、工作、娱乐的得力助手&#xff0c;但随着时代的变迁和技术的发展&#xff0c;我们需要的不仅仅是一部手机&#xff0c;而是一个更强大、更灵活的工具。在这个时候&#xff0c;云手机横空出世&#xff0c;成为了我们手机使用的新选择…

3d展览模型空间灯光怎么打---模大狮模型网

在设计3D展览模型时&#xff0c;灯光的运用至关重要。合理的空间灯光设计不仅能够烘托展品的氛围和情感&#xff0c;还可以引导观众的视线&#xff0c;增强展览的艺术感和观赏性。本文将介绍如何在3D展览模型中打造出合适的空间灯光效果&#xff0c;以提升展览的吸引力和视觉效…

《代码大全》读后感:软件开发的黄金法则

在软件开发领域&#xff0c;有一本书被誉为“圣经”&#xff0c;那就是《代码大全》。这本书由史蒂夫迈克康奈尔所著&#xff0c;于2006年首次出版&#xff0c;至今仍在全球范围内享有盛誉。它不仅为开发者们提供了详尽的编程技巧&#xff0c;更深入地探讨了软件开发过程中的各…

4.20.1 深度神经网络提高放射科医生在乳腺癌筛查中的表现

新颖的两阶段神经网络&#xff0c;用于将全局和局部信息与适当的训练过程结合起来。这使我们能够使用非常高容量的块级网络从像素级标签中学习&#xff0c;同时网络也可以从宏观乳房级标签中学习。模型可以生成可解释的热图&#xff0c;指示可疑发现的位置。即使在拥有大量图像…

SCP收容物121~130

注 &#xff1a;此文接SCP简介以及116~120的介绍,本文只供开玩笑 ,与steve_gqq_MC合作。 --------------------------------------------------------------------------------------------------------------------------------- 目录 scp-121 scp-122 scp-123 scp-124 …

C++学习第九天(list及其模拟实现)

1、list介绍 list是可以在常熟范围内任意位置进行 插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向其前一个元素和后一个元素list和forward…

Linux虚拟化性能损失:原因、评估与优化策略

在云计算和数据中心领域&#xff0c;Linux虚拟化作为基础设施的核心组件&#xff0c;为资源的高效利用和应用程序的灵活部署提供了坚实的基础。然而&#xff0c;尽管其优势显著&#xff0c;虚拟化环境下的性能损失问题仍然是一个不可忽视的挑战。本文将深入探讨Linux虚拟化中性…

nodejs 老生代和新生代如何理解

在Node.js中&#xff0c;虽然Node.js本身并不直接管理内存的具体分配与回收策略&#xff0c;但其底层依赖的JavaScript引擎V8确实实现了自动内存管理机制&#xff0c;其中包括了对内存区域的细分&#xff0c;其中就包括了“新生代”和“老生代”的概念。 新生代&#xff08;Yo…

学校开展第二届教学名师沙龙

四川城市职业学院讯 4月23日下午&#xff0c;党委教师工作部&#xff08;质量部&#xff09;、教师发展中心组织开展了以“大力弘扬教育家精神&#xff0c;建设高质量高水平教师队伍”为主题的第二届教学名师经验分享沙龙活动。全校12名入选学校教学名师&#xff08;名辅导员…

ubuntu 复制文件路径

前言 我打算搞一个ubuntu右键复制文件路径的插件&#xff0c;但是找不到&#xff0c;只能平替 这个配置&#xff0c;可以把文件拖拽到cmd窗口&#xff0c;然后就直接cmd输出文件路径 配置 cd ~ vim .bashrc 在文件结尾添加 cdd () { ddirname "$1"; echo …

Spring 注解开发详解

1. 注解驱动入门案例介绍 1.1 需求描述 1.需求&#xff1a;实现保存一条数据到数据库。 2.表结构&#xff1a;create table account(id int primary key auto_increment,name varchar(50),money double(7,2)); 3.要求&#xff1a;使用spring框架中的JdbcTemplate和DriverMana…

什么是全局污染?如何避免全局污染

全局污染通常是指在编程环境中&#xff0c;不当的变量或函数定义导致它们意外地成为了全局作用域的一部分&#xff0c;从而与预期的作用域&#xff08;如函数内部或模块内部&#xff09;产生了冲突。这种情况在JavaScript中尤为常见&#xff0c;因为JavaScript是一个函数级作用…