使用通道和模式

在这里插入图片描述

通过通道、选择语句和最佳实践掌握 Go 中的并发编程

并发编程是构建高效和响应迅速的软件的强大范例。Go,也被称为 Golang,通过通道提供了一种健壮且优雅的解决方案来进行并发通信。在这篇文章中,我们将探讨通道的概念、它们在并发编程中的作用,以及如何使用无缓冲和有缓冲的通道发送和接收数据。

通道简介

在 Go 中,通道是一种基本特性,它们使 Goroutines(并发执行的线程)之间能够进行安全和同步的通信。它们作为数据在 Goroutines 之间传递的通道,有助于并发程序的协调和同步。

通道是单向的,这意味着它们可以用于发送数据(<- chan)或接收数据(chan <-)。这种单向性有助于在 Goroutines 之间确立明确和受控的数据流。

发送和接收数据

1. 无缓冲通道

无缓冲通道 是一种数据同时发送和接收的通道类型。当在无缓冲通道上发送一个值时,发送者会阻塞,直到有一个相应的接收者准备好接收数据。同样,接收者会阻塞,直到有数据可用于接收。

以下是一个说明使用无缓冲通道的示例:

package mainimport ("fmt""time"
)func main() {ch := make(chan int) // Create an unbuffered channelgo func() {ch <- 42 // Send data into the channel}()time.Sleep(time.Second) // Give the Goroutine time to executevalue := <-ch // Receive data from the channelfmt.Println("Received:", value)
}

在这个示例中,一个 Goroutine 向无缓冲通道 ch 发送值 42,然后主 Goroutine 进行接收。程序将会阻塞,直到发送者和接收者都准备好。

2. 有缓冲通道

有缓冲通道 允许你使用指定的缓冲区大小异步地发送和接收数据。这意味着只要缓冲区没有满,你就可以向通道发送多个值而无需等待接收者。同样地,只要缓冲区不为空,接收者也可以从通道中读取数据而无需等待发送者。

以下是一个说明使用有缓冲通道的示例:

package mainimport "fmt"func main() {ch := make(chan string, 2) // Create a buffered channel with a capacity of 2ch <- "Hello" // Send data into the channelch <- "World"fmt.Println(<-ch) // Receive data from the channelfmt.Println(<-ch)
}

在这个示例中,我们创建了一个容量为 2 的有缓冲通道 ch。我们可以在不阻塞的情况下向通道发送两个值,然后接收并打印这些值。当你希望在发送者和接收者之间解耦,使它们在缓冲区大小的限制内独立工作时,有缓冲通道非常有用。

通道同步

在 Go 中,通道同步是一种通过使用通道来协调和同步 Goroutines(并发线程)执行的技术。通道促进了 Goroutines 之间的安全和有序的通信,使它们能够在特定任务完成或数据准备好时相互发出信号。这种同步机制对于确保 Goroutines 以受控和同步的方式执行至关重要。

以下是一些常见的场景,其中通道同步非常有用:

  1. 等待 Goroutines 完成:你可以使用通道来等待一个或多个 Goroutines 完成它们的任务,然后再继续主程序的执行。
  2. 协调并行任务:通道可以被用来编排多个 Goroutines 同时执行任务,确保它们按照特定的顺序完成工作或在特定点同步。
  3. 收集结果:通道可以用来收集和聚合来自多个 Goroutines 的结果,然后在所有 Goroutines 完成它们的工作后对这些结果进行处理。

让我们通过示例来探索这些场景:

1. 等待 Goroutines 完成

package mainimport ("fmt""sync"
)func worker(id int, wg *sync.WaitGroup) {defer wg.Done()fmt.Printf("Worker %d is working\n", id)
}func main() {var wg sync.WaitGroupfor i := 1; i <= 3; i++ {wg.Add(1)go worker(i, &wg)}wg.Wait() // Wait for all workers to finishfmt.Println("All workers have finished.")
}

在这个示例中,我们有三个工作 Goroutines。我们使用 sync.WaitGroup 来等待它们都完成工作后再打印“所有工作者都已完成”。

2. 协调并行任务

package mainimport ("fmt""sync"
)func main() {var wg sync.WaitGroupch := make(chan int)for i := 1; i <= 3; i++ {wg.Add(1)go func(id int) {defer wg.Done()fmt.Printf("Goroutine %d is working\n", id)ch <- id // Send a signal to the channel when done}(i)}// Wait for all Goroutines to signal completiongo func() {wg.Wait()close(ch) // Close the channel when all Goroutines are done}()for id := range ch {fmt.Printf("Received signal from Goroutine %d\n", id)}fmt.Println("All Goroutines have finished.")
}

在这个示例中,我们有三个 Goroutines 执行工作,并使用一个通道来发出它们完成的信号。我们使用 sync.WaitGroup 来等待所有 Goroutines 完成,而另一个独立的 Goroutine 则监听通道,以知道每个 Goroutine 何时完成其工作。

3. 收集结果

package mainimport ("fmt""sync"
)func worker(id int, resultChan chan<- int, wg *sync.WaitGroup) {defer wg.Done()result := id * 2resultChan <- result // Send the result to the channel
}func main() {var wg sync.WaitGroupresultChan := make(chan int, 3)for i := 1; i <= 3; i++ {wg.Add(1)go worker(i, resultChan, &wg)}wg.Wait() // Wait for all workers to finishclose(resultChan) // Close the channel when all results are sentfor result := range resultChan {fmt.Printf("Received result: %d\n", result)}
}

在这个示例中,三个工作 Goroutines 计算结果并将它们发送到一个通道。主 Goroutine 等待所有工作者完成,关闭通道,然后从通道中读取和处理结果。

这些示例说明了如何使用通道同步在 Go 中的各种并发编程场景中协调和同步 Goroutines。通道为 Goroutines 之间提供了一个强大的机制,使得编写行为可预测和可靠的并发程序变得更加容易。

选择语句:多路复用通道

管理并发任务的关键工具之一是 select 语句。在本文中,我们将探讨 select 语句在多路复用通道中的作用,这是一种使 Go 程序员有效同步和协调 Goroutines 的技术。

使用 select 进行通道的多路复用

当您有多个 Goroutines 通过各种通道进行通信时,您可能需要有效地协调它们的活动。select 语句允许您通过选择可以进行的第一个通道操作来实现这一点。

以下是一个简单的示例,演示了如何使用 select 进行通道的多路复用:

package mainimport ("fmt""time"
)func main() {ch1 := make(chan string)ch2 := make(chan string)go func() {time.Sleep(time.Second)ch1 <- "Message from Channel 1"}()go func() {time.Sleep(time.Millisecond * 500)ch2 <- "Message from Channel 2"}()select {case msg1 := <-ch1:fmt.Println(msg1)case msg2 := <-ch2:fmt.Println(msg2)}fmt.Println("Main function exits")
}

在这个示例中,我们有两个 Goroutines 在两个不同的通道 ch1ch2 上发送消息。select 语句选择第一个变得可用的通道操作,允许我们从 ch1ch2 接收并打印消息。然后程序继续执行主函数,展示了使用 select 进行通道多路复用的强大功能。

使用默认情况下的 select

select 语句还支持一个 default 情况,当您想要处理没有任何通道操作准备好的情况时,这非常有用。以下是一个示例:

package mainimport ("fmt""time"
)func main() {ch := make(chan string)go func() {time.Sleep(time.Second * 2)ch <- "Message from Channel"}()select {case msg := <-ch:fmt.Println(msg)default:fmt.Println("No message received")}fmt.Println("Main function exits")
}

在这种情况下,我们有一个 Goroutine 在通道 ch 上发送消息。然而,select 语句包括一个 default 情况,用于处理在预期时间内没有消息到达的情况。这允许对没有任何通道操作准备好的情况进行优雅的处理。

Go 中的最佳实践和模式:扇出、扇入和关闭通道

当涉及编写干净高效的 Go 代码时,有一些特定的最佳实践和模式可以显著提高您的并发程序的质量和性能。在本文中,我们将探讨两个关键的实践:扇出、扇入关闭通道。这些模式是管理 Go 应用程序中的并发和通信的强大工具。

1. 扇出、扇入

扇出、扇入 模式是一个并发设计模式,它允许您在多个 Goroutines 之间分发工作,然后收集和整合结果。当处理可以并发处理然后聚合的任务时,这种模式尤其有用。

扇出、扇入的示例
package mainimport ("fmt""math/rand""sync""time"
)func worker(id int, input <-chan int, output chan<- int) {for number := range input {// Simulate some worktime.Sleep(time.Millisecond * time.Duration(rand.Intn(100)))output <- number * 2}
}func main() {rand.Seed(time.Now().UnixNano())input := make(chan int)output := make(chan int)const numWorkers = 3var wg sync.WaitGroup// Fan-out: Launch multiple workersfor i := 0; i < numWorkers; i++ {wg.Add(1)go func(id int) {defer wg.Done()worker(id, input, output)}(i)}// Fan-in: Collect resultsgo func() {wg.Wait()close(output)}()// Send data to workersgo func() {for i := 1; i <= 10; i++ {input <- i}close(input)}()// Receive and process resultsfor result := range output {fmt.Println("Result:", result)}
}

在这个示例中,我们创建了三个工作 Goroutines 来执行一些模拟工作,然后将结果发送到一个输出通道。主 Goroutine 生成输入数据,而一个单独的 Goroutine 使用扇入模式收集和处理结果。

2. 关闭通道

关闭通道是一个重要的实践,用于标记数据传输的完成并防止 Goroutines 无限期地阻塞。当您不再计划通过它们发送数据时,关闭通道是至关重要的,以避免死锁。

关闭通道的示例
package mainimport "fmt"func main() {dataChannel := make(chan int, 3)go func() {defer close(dataChannel) // Close the channel when donefor i := 1; i <= 3; i++ {dataChannel <- i}}()// Receive data from the channelfor num := range dataChannel {fmt.Println("Received:", num)}
}

在这个示例中,我们创建了一个容量为3的带缓冲通道dataChannel。在向该通道发送三个值之后,我们使用close函数关闭它。关闭通道向任何接收者发出信号,表示不会再发送更多的数据。这使得接收的 Goroutine 在所有数据都已被处理完毕后可以优雅地退出。

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

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

相关文章

异步编程Promise

文章目录 前言一、关于 Promise 的理解与使用1.相关知识补充区别实例对象和函数对象同步回调异步回调Js中的错误&#xff08;error&#xff09;和错误处理 2.promise是什么 二、Promise 原理三、Promise 封装 Ajax四、async 与 await总结 前言 在项目中&#xff0c;promise的使…

c语言:文件操作(2),认识各种文件操作函数

fgets 作用 fgets是C语言标准库中用于从文件中读取字符串的函数。 fgets函数从指定的文件流stream中读取最多n-1个字符&#xff0c;或者直到遇到换行符&#xff08;包括换行符在内&#xff09;&#xff0c;并将其存储到以str指向的字符数组中。读取的字符串会以null字符\0结…

【小白专用】php以pdo方式连接sqlserver,开启sqlsrv扩展

一、安装ODBC程序&#xff0c; 下载适用于 SQL Server 的 ODBC 驱动程序 - 适用于 SQL Server 的 ODBC 驱动程序 |Microsoft 学习 运行安装程序&#xff0c;出现如下图所示页面&#xff1b; 选择下一步&#xff1b;选择我同意许可协议中的条款后选择下一步&#xff1b; 点击安…

22 3GPP在SHF频段基于中继的5G高速列车场景中的标准化

文章目录 信道模型实验μ参考信号初始接入方法波形比较 RRH&#xff1a;remote radio head 远程无线头 HTS&#xff1a;high speed train 高速移动列车 信道模型 考虑搭配RRH和车载中继站之间的LOS路径以及各种环境&#xff08;开放或峡谷&#xff09;&#xff0c;在本次实验场…

ARM GIC(四) gicv3架构基础

GICv3架构是GICv2架构的升级版&#xff0c;增加了很多东西。变化在于以下&#xff1a; 使用属性层次&#xff08;affinity hierarchies&#xff09;&#xff0c;来对core进行标识&#xff0c;使gic支持更多的core 将cpu interface独立出来&#xff0c;用户可以将其设计在core…

说说 style gan 中的感知路径长度(Perceptual Path Length)

我在之前的博库中介绍了 style gan 的基本原理&#xff0c;原文中有提出感知路径长度&#xff08;Perceptual Path Length&#xff09;的概念。这是一种评价生成器质量的方式。 PPL基本思想&#xff1a;给出两个随机噪声 z 1 , z 2 ​ &#xff0c;为求得两点的感知路径长度PPL…

数据可视化---离群值展示

内容导航 类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统…

HuatuoGPT模型介绍

文章目录 HuatuoGPT 模型介绍LLM4Med&#xff08;医疗大模型&#xff09;的作用ChatGPT 存在的问题HuatuoGPT的特点ChatGPT 与真实医生的区别解决方案用于SFT阶段的混合数据基于AI反馈的RL 评估单轮问答多轮问答人工评估 HuatuoGPT 模型介绍 HuatuoGPT&#xff08;华佗GPT&…

基于博弈树的开源五子棋AI教程[1 位棋盘]

文章目录 0 引子1 定义2 实现 0 引子 常见的五子棋棋盘大小为15x15&#xff0c;最直观的表示就是一个二维数据。本文为了易于拓展一开始使用的是QVector<QVector>的数据&#xff0c;但是在分支因子为10的情况下只能搜索到4层左右&#xff0c;后面深度加深&#xff0c;搜…

Vue中为什么data属性是一个函数而不是一个对象?(看完就会了)

文章目录 一、实例和组件定义data的区别二、组件data定义函数与对象的区别三、原理分析四、结论 一、实例和组件定义data的区别 vue实例的时候定义data属性既可以是一个对象&#xff0c;也可以是一个函数 const app new Vue({el:"#app",// 对象格式data:{foo:&quo…

Spring(2)Spring从零到入门 - Spring注解开发(以IoC/DI为核心)

Spring&#xff08;2&#xff09;Spring从零到入门 - Spring注解开发&#xff08;以IoC/DI为核心&#xff09; 文章目录 Spring&#xff08;2&#xff09;Spring从零到入门 - Spring注解开发&#xff08;以IoC/DI为核心&#xff09;3 Spring之IOC/DI注解开发3.1 注解开发定义be…

TrustZone之安全启动与引导失败处理

一、引导和信任链 引导是任何TrustZone系统的关键部分。只有在引导流程中之前运行的所有软件组件都是可信的情况下,才能信任某个软件组件。这通常被称为信任链。下图显示了一个简化的信任链: 在我们的示例中,首先运行的代码是boot ROM。我们必须隐式信任boot ROM,因…

iPhone手机开启地震预警功能

iPhone手机开启地震预警功能 地震预警告警开启方式 地震预警 版权&#xff1a;成都高新减灾研究所 告警开启方式

2-高可用-负载均衡、反向代理

负载均衡、反向代理 upstream server即上游服务器&#xff0c;指Nginx负载均衡到的处理业务的服务器&#xff0c;也可以称之为real server,即真实处理业务的服务器。 对于负载均衡我们要关心的几个方面如下&#xff1a; 上游服务器配置&#xff1a;使用upstream server配置上…

python可以做小程序研发嘛,python能做微信小程序吗

大家好&#xff0c;给大家分享一下python可以做微信小程序开发吗&#xff0c;很多人还不知道这一点。下面详细解释一下。现在让我们来看看&#xff01; 大家好&#xff0c;给大家分享一下用python编写一个小程序&#xff0c;很多人还不知道这一点。下面详细解释一下用python代码…

软件测试十大必问面试题(附答案和解析)

01 介绍之前负责的项目 参考答案&#xff1a;先大概描述一下这个项目是做什么的&#xff08;主要功能&#xff09;&#xff0c;包括哪些模块&#xff0c;是什么架构的&#xff08;B/S、C/S、移动端&#xff1f;&#xff09;&#xff0c;你在其中负责哪些模块的测试。期间经历了…

【Vue】el-date-picker日期范围组件(本周、本月、上周)

系列文章 【Vue】vue增加导航标签 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/134965353 【Vue】Element开发笔记 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/133947977 【Vue】vue&#xff0c;在Windows IIS平台…

智能优化算法应用:基于非洲秃鹫算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于非洲秃鹫算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于非洲秃鹫算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.非洲秃鹫算法4.实验参数设定5.算法结果6.…

光条中心线提取-Steger算法 [OpenCV]

在线结构光视觉传感器中&#xff0c;由线激光器发射出的线结构光&#xff0c;在本质上为一个连续且具有一定厚度的空间光平面&#xff0c;而在目标表面上所形成的具有一定宽度的光条特征&#xff0c;即为该光平面与目标表面相交而成的交线。在该空间光平面的厚度方向上&#xf…

IDEA创建springboot工程

选择spring boot的版本和依赖 finish创建完成 删除无用的文件