golang关键字channel介绍

Golang 关键字 channel 的用法和原理

Golang 是一门支持并发编程的语言,它提供了一种特殊的类型:channel,用于在不同的 goroutine 之间传递数据,实现同步和通信。channel 是 Go 语言高性能并发编程中的核心数据结构和机制。本文将介绍 Golang 关键字 channel 的用法和原理,包括如何创建、发送、接收、关闭、选择和避免死锁等。

channel 的创建

channel 是一种引用类型,可以使用 make 函数来创建。make 函数接受两个参数:channel 的类型和可选的缓冲区大小。例如:

ch := make(chan int) // 创建一个无缓冲的 int 类型的 channel
ch := make(chan int, 10) // 创建一个有缓冲的 int 类型的 channel,缓冲区大小为 10

channel 的类型由关键字 chan 和数据类型组成,表示该 channel 可以传递的数据类型。例如,chan int 表示一个可以传递 int 类型数据的 channel。

channel 的缓冲区大小表示该 channel 可以存储的数据的数量。如果没有指定缓冲区大小,或者指定为 0,那么该 channel 就是一个无缓冲的 channel,也就是说,每次发送或接收数据都需要等待另一端的 goroutine 准备好。如果指定了缓冲区大小,那么该 channel 就是一个有缓冲的 channel,也就是说,只有当缓冲区满了或空了的时候,发送或接收操作才会阻塞。

channel 的发送和接收

channel 的发送和接收操作使用箭头符号 <- 来表示。箭头的方向表示数据的流向。例如:

ch <- x // 将 x 发送到 channel ch 中
x <- ch // 从 channel ch 中接收数据,并赋值给 x

channel 的发送和接收操作都是原子的,也就是说,它们不会被其他的 goroutine 打断或干扰。channel 的发送和接收操作也都是阻塞的,也就是说,它们会等待对应的操作完成才会继续执行。例如:

ch <- x // 如果 ch 是无缓冲的,或者 ch 的缓冲区已满,那么这个操作会阻塞,直到有其他的 goroutine 从 ch 中接收数据
x <- ch // 如果 ch 是无缓冲的,或者 ch 的缓冲区已空,那么这个操作会阻塞,直到有其他的 goroutine 向 ch 中发送数据

channel 的发送和接收操作可以保证数据的顺序,也就是说,先发送的数据一定会先被接收,后发送的数据一定会后被接收。这是因为 channel 内部实现了一个先进先出(FIFO)的队列来存储数据。

channel 的关闭

channel 的关闭操作使用 close 函数来实现。close 函数接受一个 channel 类型的参数,表示要关闭的 channel。例如:

close(ch) // 关闭 channel ch

channel 的关闭操作可以通知其他的 goroutine 这个 channel 已经不再使用了,也就是说,不会再有数据发送到这个 channel 中。关闭一个 channel 之后,不能再向这个 channel 中发送数据,否则会导致 panic 错误。但是,仍然可以从这个 channel 中接收数据,直到 channel 中的所有数据都被接收完毕。例如:

close(ch) // 关闭 channel ch
ch <- x // panic: send on closed channel
x <- ch // 如果 ch 中还有数据,那么可以正常接收,如果 ch 中没有数据,那么会接收到零值

为了判断一个 channel 是否已经关闭,可以使用以下的语法:

x, ok <- ch // 从 channel ch 中接收数据,并赋值给 x,同时返回一个布尔值 ok,表示 channel 是否已经关闭
if !ok {// channel 已经关闭,处理逻辑
} else {// channel 还没有关闭,处理逻辑
}

为了避免内存泄漏等问题,建议在不再使用 channel 的时候,及时关闭它。一般来说,关闭 channel 的操作应该由发送方来执行,而不是接收方。这是因为发送方知道什么时候数据发送完毕,而接收方不一定知道什么时候数据接收完毕。如果多个 goroutine 都向同一个 channel 中发送数据,那么可以使用 sync.WaitGroup 来协调关闭 channel 的时机。

channel 的选择

在并发编程中,有时候我们需要同时处理多个 channel 的发送和接收操作,或者根据不同的 channel 的状态来执行不同的逻辑。这时候,我们可以使用 select 语句来实现。select 语句类似于 switch 语句,但是它的每个分支都是一个 channel 的操作。select 语句的语法如下:

select {
case x <- ch1: // 如果 ch1 可以发送数据,那么执行这个分支// 处理逻辑
case y <- ch2: // 如果 ch2 可以发送数据,那么执行这个分支// 处理逻辑
case z <- ch3: // 如果 ch3 可以接收数据,那么执行这个分支// 处理逻辑
default: // 如果以上都不满足,那么执行这个分支// 处理逻辑
}

select 语句会随机选择一个满足条件的分支执行,如果没有任何分支满足条件,那么会执行 default 分支,如果没有 default 分支,那么会阻塞,直到有一个分支满足条件。如果有多个分支满足条件,那么会随机选择一个执行。

select 语句可以用来实现多路复用、超时控制、非阻塞操作等功能。例如:

// 多路复用,同时处理多个 channel 的数据
select {
case x <- ch1:fmt.Println("received", x, "from ch1")
case y <- ch2:fmt.Println("received", y, "from ch2")
}// 超时控制,如果在指定的时间内没有收到数据,就返回
select {
case x <- ch:fmt.Println("received", x)
case <-time.After(3 * time.Second):fmt.Println("timeout")
}// 非阻塞操作,如果 channel 不可用,就执行其他逻辑
select {
case x <- ch:fmt.Println("received", x)
default:fmt.Println("no data")
}

channel 的死锁

在使用 channel 时,有一种特殊的情况,就是当所有的 goroutine 都被阻塞在 channel 的操作上,而没有其他的 goroutine 来解除阻塞,那么就会发生死锁(deadlock)。死锁会导致程序无法继续运行,甚至崩溃。例如:

package mainimport "fmt"func main() {ch := make(chan int)// 向一个无缓冲 channel 发送数据,但没有接收者ch <- 1fmt.Println("Sent data to channel")// 这里不会执行到,因为上面的发送操作会导致死锁
}

为了避免死锁,我们需要注意以下几点:

  • 不要在没有其他的 goroutine 的情况下,向一个无缓冲的 channel 发送或接收数据,否则会导致自身阻塞。
  • 不要在一个 goroutine 中,连续向同一个 channel 发送或接收多个数据,否则会导致自身阻塞,或者和其他的 goroutine 形成循环等待。
  • 不要在一个 goroutine 中,同时操作多个 channel,否则会导致自身阻塞,或者和其他的 goroutine 形成循环等待。可以使用 select 语句来避免这种情况。
  • 不要忘记关闭不再使用的 channel,否则会导致其他的 goroutine 阻塞在该 channel 上,或者造成内存泄漏。
  • 不要向一个已经关闭的 channel 发送数据,否则会导致 panic 错误。可以使用 defer 语句来确保关闭 channel 的时机。

channel 的原理

channel 的内部实现是一个结构体,它包含了以下几个字段:

  • qcount:表示 channel 中当前的数据数量
  • dataqsiz:表示 channel 的缓冲区大小
  • buf:表示 channel 的缓冲区,是一个指向数组的指针
  • elemsize:表示 channel 中数据的大小
  • closed:表示 channel 是否已经关闭
  • recvq:表示等待接收数据的 goroutine 队列
  • sendq:表示等待发送数据的 goroutine 队列
  • lock:表示 channel 的互斥锁,用来保护 channel 的状态

channel 的发送和接收操作的内部逻辑如下:

  • 发送操作:
    • 加锁
    • 如果 channel 已经关闭,那么 panic
    • 如果 channel 有缓冲区,并且缓冲区没有满,那么将数据放入缓冲区,更新 qcount,解锁,返回
    • 如果 channel 没有缓冲区,或者缓冲区已满,那么检查 recvq 是否有等待的 goroutine
      • 如果有,那么将数据直接传递给第一个等待的 goroutine,唤醒它,解锁,返回
      • 如果没有,那么将当前的 goroutine 放入 sendq,阻塞,等待被唤醒
  • 接收操作:
    • 加锁
    • 如果 channel 有缓冲区,并且缓冲区不为空,那么从缓冲区取出数据,更新 qcount,解锁,返回
    • 如果 channel 没有缓冲区,或者缓冲区为空,那么检查 sendq 是否有等待的 goroutine
      • 如果有,那么从第一个等待的 goroutine 接收数据,唤醒它,解锁,返回
      • 如果没有,那么检查 channel 是否已经关闭
        • 如果是,那么返回零值和 false,解锁,返回
        • 如果不是,那么将当前的 goroutine 放入 recvq,阻塞,等待被唤醒

总结

本文介绍了 Golang 关键字 channel 的用法和原理,包括如何创建、发送、接收、关闭、选择和避免死锁等。channel 是 Go 语言并发编程中的核心数据结构和机制,它可以实现不同的 goroutine 之间的数据传递、同步和通信。在使用 channel 时,要注意遵循一些规范和原则,以提高代码的可读性和可维护性,以及避免一些常见的错误和问题。

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

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

相关文章

如何创建测试计划?这些要考虑到

以下为作者观点&#xff1a; 创建一个彻底和有效的测试计划对软件测试的成功至关重要。它可以帮助识别过程中可能出现的潜在问题或问题。 什么是测试计划&#xff1f; 测试计划是一份文件&#xff0c;概述了软件测试过程的策略、目标、资源和时间表。测试计划通常包括一些细…

Golang 锁介绍

在并发编程中&#xff0c;锁是一种常用的同步机制&#xff0c;用来保护共享资源的安全访问和修改。Golang 作为一门支持并发的语言&#xff0c;提供了两种主要的锁类型&#xff1a;互斥锁&#xff08;Mutex&#xff09;和读写锁&#xff08;RWMutex&#xff09;。本文将介绍这两…

uniapp 项目 浏览器chrome使用vue devtool 识别不了 in not detect

问题 uniapp的项目&#xff0c;vue2&#xff0c; chrome 分析 添加了运行时&#xff0c;指定模板h5.html 指定的h5.html重置了运行根目录&#xff0c;导致了vue dev tool在运行时&#xff0c;chrome上识别不了。 解决&#xff1a; 方法1&#xff1a; 只能调试的时候,不加sati…

java常用应用程序编程接口(API)——Arrays概述

前言&#xff1a; 学到Arrays了&#xff0c;整理下心得。打好基础&#xff0c;daydayup! Arrays 用来操作数组的一个工具类 Arrays的常见方法 方法名说明public static String toString(类型[] arr)返回数组的内容public static int[ ] copyOfRange(类型[ ] arr,启示索引&…

【操作系统学习笔记】文件管理1.1

【操作系统学习笔记】文件管理1.1 参考书籍: 王道考研 视频地址: Bilibili I/O 设备的基本概念与分类 I/O 设备就是可以将数据输入到计算机&#xff0c;或者可以接收计算机输出数据的外部设备&#xff0c;属于计算机中的硬件设备。UNIX系统将外部设备抽象为一种特殊的文件&a…

2021 年 6 月青少年软编等考 C 语言一级真题解析

目录 T1. 数的输入和输出思路分析 T2. (a / b) c 的值思路分析 T3. 大写字母的判断思路分析 T4. 特殊求和思路分析 T5. 硬币翻转思路分析 T1. 数的输入和输出 输入一个整数和双精度浮点数&#xff0c;先将浮点数保留 2 2 2 位小数输出&#xff0c;然后输出整数。 时间限制&…

SpringMVC框架①

目录 一、SpringMVC概述 1. 什么是MVC 2. 什么是SpringMVC 3. springMVC特点 二、HellWorld 1. 开发环境 2. 创建maven工程 3. 配置web.xml 4. 创建请求控制器 5. 创建springMVC的配置文件 6. 测试HelloWorld 三、RequestMapping注解 1、RequestMapping功能 2、Re…

Android如何实现复制到剪贴板

文章目录 核心代码关于权限举例 在 Android 开发中&#xff0c;可以使用 ClipboardManager 来复制文本到剪贴板。以下是一个简单的示例代码&#xff0c;演示如何将文本复制到剪贴板&#xff1a; 核心代码 // 获取剪贴板管理器 val clipboardManager getSystemService(Contex…

Linux - 安装 maven(详细教程)

目录 一、下载二、安装三、配置环境变量四、镜像资源配置 一、下载 官网&#xff1a;https://maven.apache.org/download.cgi 打开 maven 的官网下载页面&#xff0c;点击 bin.tar.gz 文件链接 即可下载最新版本的 maven 如果想要下载旧版本的 meven&#xff0c;则点击 Maven…

图形系统开发实战课程:进阶篇(上)——9.空间算法(一)

图形开发学院&#xff5c;GraphAnyWhere 课程名称&#xff1a;图形系统开发实战课程&#xff1a;进阶篇(上)课程章节&#xff1a;“图形样式”原文地址&#xff1a;https://www.graphanywhere.com/graph/advanced/2-9.html 第九章 空间算法&#xff08;一&#xff09; \quad 在…

SpringBoot 自定义映射规则resultMap collection一对多

介绍 collection是封装一对多关系的&#xff0c;通常情况下是一个列表&#xff0c;association是一对一&#xff0c;通常情况是一个对象。例如&#xff1a;查询班级下所有的学生&#xff0c;一个班级可以有多个学生&#xff0c;这就是一对多。 案例 有一个学生表&#xff0c…

基于 Redis 的 JWT令牌失效方案

应用场景 当用户登录状态到登出状态时&#xff0c;对应的JWT的令牌需要设置为失效状态&#xff0c;这时可以使用基于 Redis 的黑名单方案来实现JWT令牌失效。 基于 Redis 的黑名单方案 当用户需要登出系统时&#xff0c;将用户携带的Token进行解析&#xff0c;解码出JWT令牌…

Go语言中的时间控制:定时器技术详细指南

Go语言中的时间控制&#xff1a;定时器技术详细指南 引言定时器基础创建和使用time.Timer使用time.Ticker实现周期性任务定时器的内部机制小结 使用time.Timer实现简单的定时任务创建和启动定时器停止和重置定时器定时器的实际应用小结 利用time.Ticker处理重复的定时任务创建和…

这里推荐一款unity3d人物动物控制器详细的等学会再写文章

unity3d Animal Controller 1.4.0a 动物NPC行为控制器 动物控制器&#xff08;AC&#xff09;是一个基于脚本架构的动画框架控制器。它适用于任何动物或人形角色的根运动或原地动画。 人和动物的各种动作都有; 小白必选、 我只是运行乐demo就感觉牛 demo路径&#xff1a;Asset…

Python不换行print在终端中不显示

问题描述 当使用不换行 print 即 print(‘test, end) 后立即关闭标准输出 sys.stdout open(os.devnull, w)则 print 的内容不会显示在正常的终端上&#xff08;例外是 PyCharm 中的终端能够正常显示&#xff09;。 复现问题 复现该问题的简易代码&#xff1a; import sys,…

基于pytorch的手写体识别

一、环境搭建 链接: python与深度学习——基础环境搭建 二、数据集准备 本次实验用的是MINIST数据集&#xff0c;利用MINIST数据集进行卷积神经网络的学习&#xff0c;就类似于学习单片机的点灯实验&#xff0c;学习一门机器语言输出hello world。MINIST数据集&#xff0c;可以…

【go从入门到精通】go环境安装和第一个经典程序

go下载和环境变量配置 下载地址 Go官网下载地址&#xff1a;https://golang.org/dl/All releases - The Go Programming Languagehttps://golang.org/dl/ 然后根据自己的系统环境来选择不同的安装包下载&#xff0c;下面我分别针对不同环境进行说明&#xff08;大家可以根据自…

计算机网络中常用的命令

Ping 通过发送一系列的ICMP回送数据报来确定本机与目的主机是否可以正常通信。输出中TTL代表ICMP报文存活时间&#xff0c;可以推算报文经过了几个路由器。ping失败有可能是由于Windows防火墙-IPv4回显请求没有开启。Ipconfig 获取主机的网络配置信息&#xff0c;例如IP地址、子…

LeetCode 438. 找到字符串中所有字母异位词

对于判断两个词是否为异位词&#xff0c;可以改而判断它们的词频表是否相同。基于此&#xff0c;在s串中设置滑动窗口&#xff0c;大小跟p串一样&#xff0c;移动&#xff08;剔除左边&#xff0c;增加右边&#xff09;这个窗口并实时记录下它的词频表然后与p的词频表比较。 cl…

数据库之Oracle数据导入导出

目录 一、单表导出和导入1、单表导出数据2、单表导入数据二、全表导出和导入1、远程导出全表数据2、导入本地数据三、密码带特殊字符的写法1、Windows OS写法2、Linux/Unix OS写法 四、总结 一、单表导出和导入 1、单表导出数据 --导出远程服务上的表数据 exp 用户名/密码IP…