Golang 并发机制-3:通道(channels)机制详解

并发编程是一种创建性能优化且响应迅速的软件的强大方法。Golang(也称为 Go)通过通道(channels)这一特性,能够可靠且优雅地实现并发通信。本文将揭示通道的概念,解释其在并发编程中的作用,并提供有关如何通过无缓冲通道和有缓冲通道发送或接收数据的见解。

介绍通道

在Go语言中,通道是一个基本的特性,它支持在gooutine(并发执行线程)之间进行安全和同步的通信。它们充当管道,通过它可以在程序之间传递数据,促进并发程序中的协调和同步。

通道是单向的,这意味着它们既可以用于发送数据(<- chan)也可以用于接收数据(chan <- )。这种单向性有助于在程序间实现清晰和可控的数据流。
在这里插入图片描述

发送和接收数据

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)
}

在这个例子中,一个线程将值 42发送到未缓冲的通道 ch.主线程接收它,程序将阻塞,直到发送方和接收方都准备好。

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(并发线程)的执行。通道促进了程序之间安全和有序的通信,允许它们在完成特定任务或准备好数据时相互发送信号。这种同步机制对于确保运行例程以受控和同步的方式执行至关重要。

下面是通道同步有用的一些常见场景:

  1. 等待Goroutines完成:你可以使用通道来等待一个或多个Goroutines在继续主程序之前完成它们的任务。
  2. 协调并行任务:通道可用于协调并发执行任务的多个goroutine,确保它们以特定顺序完成工作或在特定点同步。
  3. 收集结果:通道可用于收集和聚合来自多个goroutine的结果,然后在所有goroutine完成其工作后处理它们。

让我们用例子来探索这些场景:

  • 等待goroute完成
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.")
}

在这个例子中,我们有三个工作程序。我们使用“同步”。WaitGroup’,等待所有工人完成他们的工作,然后打印“所有工人都完成了”。

  • 协同并行任务
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.")
}

在本例中,我们有三个执行工作并使用通道发出完成信号的goroutine。我们使用“同步”。WaitGroup '等待所有的Goroutine完成,并且一个单独的Goroutine侦听通道以知道每个Goroutine何时完成其工作。

  • 收集结果
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)}
}

在本例中,三个工作线程程序计算结果并将其发送到通道。主程序等待所有工作线程完成,关闭通道,然后从通道读取和处理结果。

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

Select 语句: 多路复用通道(Multiplexing Channels)

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

当你有多个通过各种渠道进行通信的goroutine时,可能需要有效地协调它们的活动。“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")
}

在本例中,我们有两个在两个不同通道上发送消息的goroutine, ch1 ch2 select语句选择可用的首个通道操作,允许我们从 ch1 ch2接收和打印消息。然后,程序继续执行main函数,演示使用“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")
}

在本例中,我们有一个在通道‘ ch ’上发送消息的程序。然而,“select”语句包含一个“default”情况,用于处理在预期时间内没有消息到达的情况。这允许在没有任何通道操作就绪的情况下进行优雅的处理。

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

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

在这里插入图片描述
在这里插入图片描述
  1. 扇出,扇入

Fan-out, Fan-in模式是一种并发设计模式,允许你跨多个goroutine分发工作,然后收集和合并结果。当处理可以并发处理然后进行聚合的任务时,此模式特别有用。

  • 扇出,扇入示例
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)}
}

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

  1. 关闭通道

关闭通道是发送数据传输完成信号和防止程序无限阻塞的基本做法。当你不再打算通过通道发送数据时,关闭通道以避免死锁是至关重要的。

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 ’函数关闭它。关闭任何接收器的通道信号,不再发送数据。这允许接收程序在处理完所有数据后优雅地退出。

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

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

相关文章

笔记:使用ST-LINK烧录STM32程序怎么样最方便?

一般板子在插件上&#xff0c; 8脚 3.3V;9脚 CLK;10脚 DIO;4脚GND ST_Link 19脚 3.3V;9脚 CLK;7脚 DIO;20脚 GND 烧录软件&#xff1a;ST-LINK Utility&#xff0c;Keil_5; ST_Link 接口针脚定义&#xff1a; 按定义连接ST_Link与电路板&#xff1b; 打开STM32 ST-LINK Uti…

网络测试工具

工具介绍&#xff1a; 这是一个功能完整的网络测速工具&#xff0c;可以测试网络的下载速度、上传速度和延迟。 功能特点&#xff1a; 1. 速度测试 - 下载速度测试 - 上传速度测试 - Ping延迟测试 - 自动选择最佳服务器 2. 实时显示 - 进度条显示测试进度 - 实时显示测试状…

java每日精进1.31(SpringSecurity)

在所有的开发的系统中&#xff0c;都必须做认证(authentication)和授权(authorization)&#xff0c;以保证系统的安全性。 一、基础使用 1.依赖 <dependencies><!-- 实现对 Spring MVC 的自动化配置 --><dependency><groupId>org.springframework.bo…

17.2 图形绘制8

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 17.2.10 重绘 先看以下例子&#xff1a; 【例 17.28】【项目&#xff1a;code17-028】绘制填充矩形。 private void button1_Clic…

自定义数据集 使用pytorch框架实现逻辑回归并保存模型,然后保存模型后再加载模型进行预测,对预测结果计算精确度和召回率及F1分数

import numpy as np import torch import torch.nn as nn import torch.optim as optim from sklearn.metrics import precision_score, recall_score, f1_score# 数据准备 class1_points np.array([[1.9, 1.2],[1.5, 2.1],[1.9, 0.5],[1.5, 0.9],[0.9, 1.2],[1.1, 1.7],[1.4,…

neo4j入门

文章目录 neo4j版本说明部署安装Mac部署docker部署 neo4j web工具使用数据结构图数据库VS关系数据库 neo4j neo4j官网Neo4j是用ava实现的开源NoSQL图数据库。Neo4作为图数据库中的代表产品&#xff0c;已经在众多的行业项目中进行了应用&#xff0c;如&#xff1a;网络管理&am…

Java基础——分层解耦——IOC和DI入门

目录 三层架构 Controller Service Dao ​编辑 调用过程 面向接口编程 分层解耦 耦合 内聚 软件设计原则 控制反转 依赖注入 Bean对象 如何将类产生的对象交给IOC容器管理&#xff1f; 容器怎样才能提供依赖的bean对象呢&#xff1f; 三层架构 Controller 控制…

智慧园区系统集成解决方案引领未来城市管理的智能化转型

内容概要 在现代城市管理的背景下&#xff0c;“智慧园区系统集成解决方案”正扮演着越来越重要的角色。这种解决方案不仅仅是技术上的创新&#xff0c;更是一种全新的管理理念&#xff0c;它旨在通过高效的数据整合与分析&#xff0c;优化资源配置&#xff0c;提升运营效率。…

99.24 金融难点通俗解释:MLF(中期借贷便利)vs LPR(贷款市场报价利率)

目录 0. 承前1. 什么是MLF&#xff1f;1.1 专业解释1.2 通俗解释1.3 MLF的三个关键点&#xff1a; 2. 什么是LPR&#xff1f;2.1 专业解释2.2 通俗解释2.3 LPR的三个关键点&#xff1a; 3. MLF和LPR的关系4. 传导机制4.1 第一步&#xff1a;央行调整MLF4.2 第二步&#xff1a;银…

【VM】VirtualBox安装CentOS8虚拟机

阅读本文前&#xff0c;请先根据 VirtualBox软件安装教程 安装VirtualBox虚拟机软件。 1. 下载centos8系统iso镜像 可以去两个地方下载&#xff0c;推荐跟随本文的操作用阿里云的镜像 centos官网&#xff1a;https://www.centos.org/download/阿里云镜像&#xff1a;http://…

家居EDI:Hom Furniture EDI需求分析

HOM Furniture 是一家成立于1977年的美国家具零售商&#xff0c;总部位于明尼苏达州。公司致力于提供高品质、时尚的家具和家居用品&#xff0c;满足各种家庭和办公需求。HOM Furniture 以广泛的产品线和优质的客户服务在市场上赢得了良好的口碑。公司经营的产品包括卧室、客厅…

【VUE案例练习】前端vue2+element-ui,后端nodo+express实现‘‘文件上传/删除‘‘功能

近期在做跟毕业设计相关的数据后台管理系统&#xff0c;其中的列表项展示有图片展示&#xff0c;添加/编辑功能有文件上传。 “文件上传/删除”也是我们平时开发会遇到的一个功能&#xff0c;这里分享个人的实现过程&#xff0c;与大家交流谈论~ 一、准备工作 本次案例使用的…

VLN视觉语言导航基础

0 概述 视觉语言导航模型旨在构建导航决策模型 π π π&#xff0c;在 t t t时刻&#xff0c;模型能够根据指令 W W W、历史轨迹 τ { V 1 , V 2 , . . . , V t − 1 } \tau\{V_1,V_2,...,V_{t-1}\} τ{V1​,V2​,...,Vt−1​}和当前观察 V t { P t , R t , N ( V t ) } V_…

Flux的三步炼丹炉——fluxgym(三):矩阵测试

前面两篇文章给大家介绍了如何准备素材和怎么炼丹&#xff0c;现在我们拿到训练完成后的多个Lora怎么才能确定哪个才是我们需要的、效果最好的呢&#xff1f;答案就是使用xyz图表测试&#xff0c;也称为矩阵测试&#xff0c;通过控制控制变量的方法对Lora模型批量生图&#xff…

利用Muduo库实现简单且健壮的Echo服务器

一、muduo网络库主要提供了两个类&#xff1a; TcpServer&#xff1a;用于编写服务器程序 TcpClient&#xff1a;用于编写客户端程序 二、三个重要的链接库&#xff1a; libmuduo_net、libmuduo_base、libpthread 三、muduo库底层就是epoll线程池&#xff0c;其好处是…

文件读写操作

写入文本文件 #include <iostream> #include <fstream>//ofstream类需要包含的头文件 using namespace std;void test01() {//1、包含头文件 fstream//2、创建流对象ofstream fout;/*3、指定打开方式&#xff1a;1.ios::out、ios::trunc 清除文件内容后打开2.ios:…

C++编程语言:抽象机制:模板(Bjarne Stroustrup)

目录 23.1 引言和概观(Introduction and Overview) 23.2 一个简单的字符串模板(A Simple String Template) 23.2.1 模板的定义(Defining a Template) 23.2.2 模板实例化(Template Instantiation) 23.3 类型检查(Type Checking) 23.3.1 类型等价(Type Equivalence) …

无人机图传模块 wfb-ng openipc-fpv,4G

openipc 的定位是为各种模块提供底层的驱动和linux最小系统&#xff0c;openipc 是采用buildroot系统编译而成&#xff0c;因此二次开发能力有点麻烦。为啥openipc 会用于无人机图传呢&#xff1f;因为openipc可以将现有的网络摄像头ip-camera模块直接利用起来&#xff0c;从而…

【JavaEE进阶】图书管理系统 - 壹

目录 &#x1f332;序言 &#x1f334;前端代码的引入 &#x1f38b;约定前后端交互接口 &#x1f6a9;接口定义 &#x1f343;后端服务器代码实现 &#x1f6a9;登录接口 &#x1f6a9;图书列表接口 &#x1f384;前端代码实现 &#x1f6a9;登录页面 &#x1f6a9;…

【算法设计与分析】实验8:分支限界—TSP问题

目录 一、实验目的 二、实验环境 三、实验内容 四、核心代码 五、记录与处理 六、思考与总结 七、完整报告和成果文件提取链接 一、实验目的 掌握分支界限求解问题的思想&#xff1b;针对不同的问题&#xff0c;能够利用分支界限法进行问题拆分和求解以及时间复杂度分析…