使用通道和模式

在这里插入图片描述

通过通道、选择语句和最佳实践掌握 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.hqwc.cn/news/293606.html

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

相关文章

海量搜索需求助推,跟着官方和优秀创作者一起来“百度夜校”开课!

文章目录 &#x1f354;概述&#x1f6f8;新中产消费人群占比高&#xff0c;教育博主快速崭露头角&#x1f6f8;亿级消费入口&#xff0c;泛知识创作者的新蓝海 &#x1f354;概述 近日&#xff0c;百度百家号官方开启“教育变现专场”和“泛知识专场”两场爆款公开课的讲座&a…

vscode中vue项目报错

当在vscode中写代码时&#xff0c;报错报错报错......... 已经头大&#xff0c;还没写就报错&#xff0c; 这是因为eslint对语法的要求太过严格导致的编译时&#xff0c;出现各种语法格式错误 我们打开vue.config.js&#xff0c;加上这句代码&#xff0c;就OK啦 lintOnSave:…

DDPM推导笔记

各位佬看文章之前&#xff0c;可以先去看看这个视频&#xff0c;并给这位up主点赞投币&#xff0c;这位佬讲解的太好了&#xff1a;大白话AI 1.前置知识的学习 1.1 正态分布特性 ​ &#xff08;1&#xff09;正态分布的概率密度函数 f ( x ) 1 2 π σ e − ( x − μ ) …

【深入解析spring cloud gateway】12 gateway参数调优与分析

本节主要对网关主要的一些参数做一些解释说明&#xff0c;并用压测工具测试一下网关的接口&#xff0c;通过压测来验证参数配置是否合理 一、连接池参数 参数示例 spring:application:name: gatewaycloud:gateway:# http连接设置httpclient:# 全局的响应超时时间&#xff0c…

aws-waf-cdn 基于规则组的永黑解决方案

1. 新建waf 规则组 2. 为规则组添加规则 根据需求创建不同的规则 3. waf中附加规则组 &#xff08;此时规则组所有规则都会附加到waf中&#xff0c;但是不会永黑&#xff09; 此刻&#xff0c;可以选择测试下规则是否生效&#xff0c;测试前确认保护资源绑定无误 4. 创建堆…

加密算法学习

最近在写一些加密的东西。所以就整理一下常见的加密算法。 欢迎帮助纠错&#xff0c;谢谢。 废话不多直接上图&#xff1a; 加密学习一级介绍描述常见算法常见算法细分非对称加密解释 非对称加密需要两个密钥&#xff1a;公钥 (publickey) 和私钥 (privatekey)。公钥和私钥是…

分子生成工具 - ResGen 评测

ResGen 模型是浙江大学药学院侯廷军老师课题组2023年发表在nature machine intelligence期刊上文章Nature Machine Intelligence | Volume 5 | September 2023 | 1020–1030&#xff0c;题目为&#xff1a;《ResGen is a pocket-aware 3D molecular generation model based on …

关于使用libnet时性能下降的问题分析

Libnet是一个用于构建和注入网络数据包的便携式框架。它提供了在IP层和链路层创建数据包的功能&#xff0c;以及一系列辅助和补充功能。Libnet非常适合编写网络工具和网络测试代码。一些使用libnet的项目包括arping、ettercap、ipguard、isic、nemesis、packit、tcptraceroute和…

力扣日记12.21【二叉树篇】98. 验证二叉搜索树

力扣日记&#xff1a;【二叉树篇】98. 验证二叉搜索树 日期&#xff1a;2023.12.21 参考&#xff1a;代码随想录、力扣 98. 验证二叉搜索树 题目描述 难度&#xff1a;中等 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义…

STM32能够做到数据采集和发送同时进行吗?

STM32能够做到数据采集和发送同时进行吗&#xff1f; 在开始前我有一些资料&#xff0c;是我根据自己从业十年经验&#xff0c;熬夜搞了几个通宵&#xff0c;精心整理了一份「STM32的资料从专业入门到高级教程工具包」&#xff0c;点个关注&#xff0c;全部无偿共享给大家&…

jetson nano系列安装nomachine

引言&#xff1a;项目需要实现远程控制和可视化&#xff0c;在实现了4G通信的基础上&#xff0c;使用namochine实现远程的可视化和操作 1、硬件&#xff1a;jetson orin nano 8G 2、环境&#xff1a;ubuntu20.04ros-noetic 注意&#xff1a;项目主要是为了方便客户端连接到这…

2000+线下门店数字化转型,盘活近500+门店账号!

伴随着社交媒体的快速发展&#xff0c;消费者的注意力开始往线上转移。社交媒体在消费者购买决策过程中发挥着越来越重要的作用&#xff0c;逐渐成为大家获取信息、产品种草并购买下单的平台。 今年双十一期间&#xff0c;抖音商城多数品类销售额均呈现大幅增长趋势&#xff0c…