掌握Go语言:Go语言通道,并发编程的利器与应用实例(20)
通道(Channel)是用来在 Go 程序中传递数据的一种数据结构。它是一种类型安全的、并发安全的、阻塞式的数据传输方式,用于在不同的 Go 协程之间传递消息。
基本概念
- 创建通道:使用
make()函数创建一个通道。
ch := make(chan int) // 创建一个整型通道
- 发送数据:使用
<-操作符向通道发送数据。
ch <- 42 // 将整数42发送到通道ch中
- 接收数据:使用
<-操作符从通道接收数据。
x := <-ch // 从通道ch中接收数据并赋值给变量x
- 关闭通道:使用
close()函数关闭一个通道。
close(ch) // 关闭通道ch
应用场景
通道在 Go 语言中的应用非常广泛,常见的应用场景包括:
- 协程间通信:在不同的 Go 协程之间传递数据。
- 控制并发:使用通道来控制并发执行的数量,避免资源竞争。
- 数据传输:用于在不同协程之间传输数据,例如从生产者协程发送数据到消费者协程。
示例:
package mainimport "fmt"func main() {// 创建一个整型通道ch := make(chan int)// 启动一个协程发送数据到通道go func() {ch <- 42 // 发送整数42到通道}()// 从通道接收数据并打印fmt.Println(<-ch) // 输出:42
}
Go语言通道并发编程
在Go语言中,通道广泛应用于并发编程,用于在不同的协程之间安全地传递数据。
并发安全性:
- 同步操作:通道上的发送和接收操作是原子性的,保证了数据的一致性和可靠性。
- 阻塞机制:当通道为空时,接收操作会阻塞等待数据;当通道满时,发送操作会阻塞等待空间。
示例:
package mainimport ("fmt""time"
)func main() {ch := make(chan int) // 创建一个整型通道// 启动一个协程发送数据到通道go func() {for i := 0; i < 5; i++ {ch <- i // 发送整数到通道time.Sleep(time.Second) // 模拟耗时操作}close(ch) // 关闭通道}()// 从通道接收数据并打印for num := range ch {fmt.Println("Received:", num)}
}
并发编程示例:
package mainimport ("fmt""sync"
)func main() {ch := make(chan int) // 创建一个整型通道var wg sync.WaitGroup// 启动3个协程向通道发送数据for i := 0; i < 3; i++ {wg.Add(1)go func(id int) {defer wg.Done()for j := 0; j < 5; j++ {ch <- id*10 + j // 发送数据到通道}}(i)}// 启动一个协程从通道接收数据go func() {wg.Wait()close(ch)}()// 从通道接收数据并打印for num := range ch {fmt.Println("Received:", num)}
}
上面这段代码演示了使用通道在 Go 语言中进行并发编程的示例。让我们逐步解释它:
-
导入包:
import ("fmt""sync" )导入了
fmt和sync包。fmt包用于格式化输出,sync包提供了同步功能,其中sync.WaitGroup类型用于等待一组协程执行完毕。 -
main 函数:
func main() {// 创建一个整型通道ch := make(chan int)// 创建一个等待组var wg sync.WaitGroup在
main函数中,首先创建了一个整型通道ch,用于协程之间的数据传输。然后创建了一个sync.WaitGroup类型的变量wg,用于等待所有协程执行完毕。 -
启动协程发送数据:
for i := 0; i < 3; i++ {wg.Add(1) // 增加等待组计数go func(id int) {defer wg.Done() // 协程执行完毕时减少等待组计数for j := 0; j < 5; j++ {ch <- id*10 + j // 发送数据到通道}}(i) // 使用闭包保证每个协程的id不同}这段代码启动了 3 个协程,每个协程都会向通道
ch中发送一系列整数。在每个协程内部,wg.Add(1)用于增加等待组的计数,表示有一个新的协程加入;defer wg.Done()则表示协程执行完毕时减少等待组的计数,使用defer关键字确保在函数退出时执行。每个协程会循环 5 次,每次发送一个整数到通道ch中,整数的值为协程的 id 乘以 10 再加上循环变量j。 -
启动协程接收数据:
go func() {wg.Wait() // 等待所有协程执行完毕close(ch) // 关闭通道}()在这里,启动了一个新的协程,用于等待所有的发送协程执行完毕,并在等待完成后关闭通道
ch。wg.Wait()会阻塞,直到所有协程执行完毕。 -
从通道接收数据并打印:
for num := range ch {fmt.Println("Received:", num)}最后,使用
range关键字从通道ch中循环接收数据,并将接收到的数据打印出来。由于通道已经在发送协程执行完毕后关闭了,因此在所有数据都被接收完毕后,range循环会自动结束。
这样,该程序就完成了在多个协程之间安全地发送和接收数据的任务,展示了 Go 语言中使用通道进行并发编程的基本方法。
进销存通道并发实例
在一个进销存系统中,通道可以用于并发处理订单和库存的管理。下面是一个简化的示例,展示了如何使用通道来处理订单和库存的并发操作:
package mainimport ("fmt""time"
)type Order struct {ID intQuantity int
}func processOrders(orders <-chan Order, stock chan<- int) {for order := range orders {// 模拟处理订单的过程fmt.Printf("Processing order %d...\n", order.ID)time.Sleep(2 * time.Second) // 模拟处理订单所需的时间// 减少库存量stock <- order.Quantity}close(stock)
}func main() {orders := make(chan Order)stock := make(chan int)// 启动一个协程来处理订单go processOrders(orders, stock)// 模拟订单生成go func() {for i := 1; i <= 5; i++ {order := Order{ID: i, Quantity: 1}orders <- orderfmt.Printf("Order %d placed.\n", i)}close(orders)}()// 更新库存totalStock := 10for quantity := range stock {totalStock -= quantityfmt.Printf("Stock updated. Remaining: %d\n", totalStock)}fmt.Println("All orders processed.")
}
这段代码演示了一个简单的进销存系统,其中使用了 Go 语言中的通道来处理订单和更新库存。
- 定义订单结构体:
type Order struct {ID int // 订单IDQuantity int // 订单数量
}
订单结构体包含订单的 ID 和数量。
- 处理订单的函数:
func processOrders(orders <-chan Order, stock chan<- int) {for order := range orders {fmt.Printf("Processing order %d...\n", order.ID)time.Sleep(2 * time.Second) // 模拟处理订单所需的时间stock <- order.Quantity // 将订单中的数量发送到库存通道}close(stock) // 关闭库存通道
}
processOrders 函数接收两个通道作为参数:orders 通道用于接收订单,stock 通道用于发送库存更新信息。函数从 orders 通道中循环接收订单,模拟处理订单的过程,并将订单中的数量发送到 stock 通道中。
- 主函数:
func main() {orders := make(chan Order) // 创建订单通道stock := make(chan int) // 创建库存通道// 启动一个协程来处理订单go processOrders(orders, stock)// 模拟订单生成go func() {for i := 1; i <= 5; i++ {order := Order{ID: i, Quantity: 1}orders <- orderfmt.Printf("Order %d placed.\n", i)}close(orders) // 关闭订单通道}()// 更新库存totalStock := 10 // 初始库存量for quantity := range stock {totalStock -= quantityfmt.Printf("Stock updated. Remaining: %d\n", totalStock)}fmt.Println("All orders processed.")
}
在 main 函数中,我们创建了订单通道 orders 和库存通道 stock。然后启动了一个协程来处理订单,使用匿名函数模拟订单生成过程,并将订单发送到 orders 通道中。接着,在主函数中从 stock 通道中接收库存更新信息,并更新库存量。当所有订单处理完毕后,程序输出 “All orders processed.”。
通过使用通道,可以实现订单的并发处理和库存的实时更新,提高系统的效率和响应速度。
Go语言通道的注意事项
注意事项:
- 避免死锁:当发送和接收操作的数量不匹配时,可能会发生死锁。例如,发送者发送数据到已经关闭的通道,或者接收者从空通道接收数据。
示例:
package mainimport "fmt"func main() {ch := make(chan int) // 创建一个整型通道close(ch) // 关闭通道// 发送数据到已关闭的通道会导致panicch <- 42
}
- 通道的阻塞:当通道为空时,接收操作会阻塞等待数据;当通道满时,发送操作会阻塞等待空间。
示例:
package mainimport "fmt"func main() {ch := make(chan int, 1) // 创建一个容量为1的整型通道ch <- 42 // 发送数据到通道ch <- 43 // 发送第二个数据到通道,因为通道已满,会导致阻塞fmt.Println("Data sent to channel")
}
总结
Go语言的通道是一种简单、高效的并发编程模型,提供了安全的数据传递和同步机制。通过通道,可以方便地实现不同 goroutine 之间的数据交流和协作,避免了共享数据的竞争和锁的复杂性。在并发编程中,通道是一种重要的组件,可以大大简化并发编程的复杂性,提高程序的可读性和可维护性。
通过了解通道的基本操作和特性,并结合实际场景,可以更好地应用通道来实现并发编程,提高程序的性能和稳定性。同时,需要注意避免常见的问题,如死锁和通道的关闭,以确保程序的正确性和健壮性。
相关文章:
掌握Go语言:Go语言通道,并发编程的利器与应用实例(20)
通道(Channel)是用来在 Go 程序中传递数据的一种数据结构。它是一种类型安全的、并发安全的、阻塞式的数据传输方式,用于在不同的 Go 协程之间传递消息。 基本概念 创建通道:使用make()函数创建一个通道。 ch : make(chan int)…...
JavaSE(上)-Day9
JavaSE(上)-Day9 集合static静态变量静态方法静态方法的注意事项重新认识main方法 继承继承注意事项子类到底能继承父类哪些内容继承中成员变量和成员方法的访问特点重写构造方法的访问特点this & super 集合 因为数组是不可变的,我们在…...
Java 内存模型概述
Java 内存区域 引言: 在并发编程中,需要解决两个问题:线程之间如何通信和线程之间如何同步 通信是指线程之间以何种机制来交换信息 在命令式编程中,通信机制主要分为两种:共享内存和消息传递 Java 的并发采用的是…...
远程桌面安卓版下载 安卓远程控制免费版
远程桌面安卓版下载与安卓远程控制免费版的应用解析 随着移动互联网的快速发展,远程桌面应用逐渐成为了许多用户、特别是技术爱好者和商务人士的必备工具。它们不仅可以在电脑上实现远程控制,还能将这种功能延伸到移动设备上,如安卓手机和平…...
算法打卡day18|二叉树篇07|Leetcode 530.二叉搜索树的最小绝对差、501.二叉搜索树中的众数、236. 二叉树的最近公共祖先
算法题 Leetcode 530.二叉搜索树的最小绝对差 题目链接:530.二叉搜索树的最小绝对差 大佬视频讲解:二叉搜索树的最小绝对差视频讲解 个人思路 因为是在二叉搜索树求绝对差,而二叉搜索树是有序的,那就把它想成在一个有序数组上求最值&…...
MySQL 中的自增ID及其应用场景
在MySQL中,自增ID主要体现在几种不同的场景下,每种自增ID都有其特定用途和行为特征: 1. Auto-Increment ID (PRIMARY KEY AUTO_INCREMENT) 场景:在创建表时,可以为某个整数字段设置AUTO_INCREMENT属性,生成…...
ChatGPT高效完成简历制作[中篇4]-有爱AI实战教程(十一)
演示站点: https://ai.uaai.cn 对话模块 官方论坛: www.jingyuai.com 京娱AI 一、导读: 在使用 ChatGPT 时,当你给的指令越精确,它的回答会越到位,举例来说,假如你要请它帮忙写文案,…...
5.2.5、【AI技术新纪元:Spring AI解码】VertexAI Embeddings
基于Models REST API的PaLM API允许开发者利用下一代大型语言模型PaLM构建生成式AI应用。大型语言模型(LLMs)是一种强大的、多用途的机器学习模型,通过一系列提示使计算机能够理解和生成自然语言。PaLM API基于Google的下一代LLM PaLM,擅长多种任务,包括代码生成、推理和文…...
【vue baidu-map】实现百度地图展示基地,鼠标悬浮标注点展示详细信息
实现效果如下: 自用代码记录 <template><div class"map" style"position: relative;"><baidu-mapid"bjmap":scroll-wheel-zoom"true":auto-resize"true"ready"handler"><bm-mar…...
uniapp canvas文字和元素居中
文字居中:ctx.textAlign "center"; 元素居中:ctx.arc(screenWidth / 2, 122, 40, 0, 2 * Math.PI); ctx.arc()的x轴为当前屏幕的宽度/2; let screenWidth 540; let screenHeight 960; // 头像 if (photoimg) {ctx.setFillSty…...
深度探索:SWAT模型和生物地球化学循环模型实现流域生态系统水-碳-氮耦合过程模拟
目录 专题一 流域水碳氮建模概述 专题二 ArcGIS入门 专题三 SWAT模型建模流程 专题四 DEM数据制备流程 专题五 土地利用数据制备流程 专题六 土壤数据制备流程 专题七 气象数据制备流程 专题八 农业措施数据制备流程 专题九 参数率定与结果验证 专题十 CENTURY模型建…...
C语言经典算法-5
文章目录 其他经典例题跳转链接26.约瑟夫问题(Josephus Problem)27.排列组合28.格雷码(Gray Code)29.产生可能的集合30.m元素集合的n个元素子集 其他经典例题跳转链接 C语言经典算法-1 1.汉若塔 2. 费式数列 3. 巴斯卡三角形 4. …...
python与excel第二节
python与excel第二节 打开一个工作簿 例子: import xlwings as xw app xw.App(visibleTrue,add_bookFalse) workbook app.books.open(rD:\TEST\python与excel\工作簿test0.xlsx) 上面例子打开了工作簿test0.xlsx。 但是,如果该excel文件不存在则报错…...
Google云计算原理与应用(四)
目录 七、海量数据的交互式分析工具Dremel(一)产生背景(二)数据模型(三)嵌套式的列存储(四)查询语言与执行(五)性能分析(六)小结 八、…...
面试常问:为什么 Vite 速度比 Webpack 快
前言 最近作者在学习 webpack 相关的知识,之前一直对这个问题不是特别了解,甚至讲不出个123....,这个问题在面试中也是常见的,作者在学习的过程当中总结了以下几点,在这里分享给大家看一下,当然最重要的是…...
principles of network applications网络应用原理
Creating a network app write programs that: ▪ run on (different) end systems ▪ communicate over network ▪ e.g., web server software communicates with browser software application transport network data link physical application transport network data li…...
QT增加线程函数步骤流程
在使用线程的时候,不仅要关注线程开启的时机,同时还要关注线程安全退出,这样才能保证程序的健壮性,如果线程开启的较多,且开启关闭比较频繁,建议使用线程池来处理。开启线程有三种方式:第一种C的…...
Python基础----字符串(持续更新中)
字符串的介绍 定义:是python中常用的数据类型之一,可以使用单引号、双引号、三引号来进行创建 字符串的标识类型:str 字符串的特性 字符串属于不可变数据类型,不能直接修改字符串的本身 数字、元组也属于不可变数据类型 字符串…...
【论文阅读】DiffSpeaker: Speech-Driven 3D Facial Animation with Diffusion Transformer
DiffSpeaker: 使用扩散Transformer进行语音驱动的3D面部动画 code:GitHub - theEricMa/DiffSpeaker: This is the official repository for DiffSpeaker: Speech-Driven 3D Facial Animation with Diffusion Transformer paper:https://arxiv.org/pdf/…...
NVM使用教程
文章目录 ⭐️写在前面的话⭐️1、卸载已经安装的node2、卸载nvm3、安装nvm4、配置路径以及下载源5、使用nvm下载node6、nvm常用命令7、全局安装npm、cnpm8、使用淘宝镜像cnpm9、配置全局的node仓库🚀 先看后赞,养成习惯!🚀&#…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
