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(并发线程)的执行。通道促进了程序之间安全和有序的通信,允许它们在完成特定任务或准备好数据时相互发送信号。这种同步机制对于确保运行例程以受控和同步的方式执行至关重要。
下面是通道同步有用的一些常见场景:
- 等待Goroutines完成:你可以使用通道来等待一个或多个Goroutines在继续主程序之前完成它们的任务。
- 协调并行任务:通道可用于协调并发执行任务的多个goroutine,确保它们以特定顺序完成工作或在特定点同步。
- 收集结果:通道可用于收集和聚合来自多个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应用程序中管理并发性和通信的强大工具。
![]() | |
![]() | |
- 扇出,扇入
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,它们执行一些模拟工作,然后将结果发送到输出通道。主程序生成输入数据,单独的程序使用扇入模式收集和处理结果。
- 关闭通道
关闭通道是发送数据传输完成信号和防止程序无限阻塞的基本做法。当你不再打算通过通道发送数据时,关闭通道以避免死锁是至关重要的。
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 ’函数关闭它。关闭任何接收器的通道信号,不再发送数据。这允许接收程序在处理完所有数据后优雅地退出。
相关文章:

Golang 并发机制-3:通道(channels)机制详解
并发编程是一种创建性能优化且响应迅速的软件的强大方法。Golang(也称为 Go)通过通道(channels)这一特性,能够可靠且优雅地实现并发通信。本文将揭示通道的概念,解释其在并发编程中的作用,并提供…...
kamailio的kamctl的使用
kamctl 是 Kamailio SIP 服务器的管理工具,用于执行各种管理任务,如启动、停止、重启 Kamailio 进程,管理用户、ACL、路由、信任的 IP 地址等。以下是对 kamctl 命令的解释及举例说明: 1. 启动、停止、重启 Kamailio start: 启动…...

HarmonyOS:ArkWeb进程
ArkWeb是多进程模型,分为应用进程、Web渲染进程、Web GPU进程、Web孵化进程和Foundation进程。 说明 Web内核没有明确的内存大小申请约束,理论上可以无限大,直到被资源管理释放。 ArkWeb进程模型图 应用进程中Web相关线程(应用唯一) 应用进程为主进程。包含网络线程、Vi…...
UI线程用到COM只能选单线程模型
无论用不用UI库,哪怕是用Win32 API手搓UI,UI线程要用COM的话,必须初始化为单线程单元(STA),即CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);,不能用MULTITHREADTHREADED。 实际上,很多(WPF等)UI库若…...

LLMs之DeepSeek:Math-To-Manim的简介(包括DeepSeek R1-Zero的详解)、安装和使用方法、案例应用之详细攻略
LLMs之DeepSeek:Math-To-Manim的简介(包括DeepSeek R1-Zero的详解)、安装和使用方法、案例应用之详细攻略 目录 Math-To-Manim的简介 1、特点 2、一个空间推理测试—考察不同大型语言模型如何解释和可视化空间关系 3、DeepSeek R1-Zero的简介:处理更…...
在C语言中使用条件变量实现线程同步
互斥量、原子操作都是实现线程同步的方法,今日介绍使用条件变量来实现线程同步。在多线程应用中,当某个线程的执行依赖于另一个线程对数据的处理时,这个线程可能没有被阻塞,只是不断地检查某个条件是否成立了(这个条件…...
图书管理系统 Axios 源码__新增图书
目录 功能介绍 核心代码解析 源码:新增图书功能 总结 本项目基于 HTML、Bootstrap、JavaScript 和 Axios 开发,实现了图书的增删改查功能。以下是新增图书的功能实现,适合前端开发学习和项目实践。 功能介绍 用户可以通过 模态框…...

Maven全解析:从基础到精通的实战指南
概念: Maven 是跨平台的项目管理工具。主要服务基于 Java 平台的构建,依赖管理和项目信息管理项目构建:高度自动化,跨平台,可重用的组件,标准化的流程 依赖管理: 对第三方依赖包的管理…...

数据密码解锁之DeepSeek 和其他 AI 大模型对比的神秘面纱
本篇将揭露DeepSeek 和其他 AI 大模型差异所在。 目录 编辑 一本篇背景: 二性能对比: 2.1训练效率: 2.2推理速度: 三语言理解与生成能力对比: 3.1语言理解: 3.2语言生成: 四本篇小结…...

python算法和数据结构刷题[5]:动态规划
动态规划(Dynamic Programming, DP)是一种算法思想,用于解决具有最优子结构的问题。它通过将大问题分解为小问题,并找到这些小问题的最优解,从而得到整个问题的最优解。动态规划与分治法相似,但区别在于动态…...

Ollama+OpenWebUI部署本地大模型
OllamaOpenWebUI部署本地大模型 前言 Ollama是一个强大且易于使用的本地大模型推理框架,它专注于简化和优化大型语言模型(LLMs)在本地环境中的部署、管理和推理工作流。可以将Ollama理解为一个大模型推理框架的后端服务。 Ollama Ollama安…...

Python从0到100(八十六):神经网络-ShuffleNet通道混合轻量级网络的深入介绍
前言: 零基础学Python:Python从0到100最新最全教程。 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…...

【网络】传输层协议TCP(重点)
文章目录 1. TCP协议段格式2. 详解TCP2.1 4位首部长度2.2 32位序号与32位确认序号(确认应答机制)2.3 超时重传机制2.4 连接管理机制(3次握手、4次挥手 3个标志位)2.5 16位窗口大小(流量控制)2.6 滑动窗口2.7 3个标志位 16位紧急…...

海思ISP开发说明
1、概述 ISP(Image Signal Processor)图像信号处理器是专门用于处理图像信号的硬件或处理单元,广泛应用于图像传感器(如 CMOS 或 CCD 传感器)与显示设备之间的信号转换过程中。ISP通过一系列数字图像处理算法完成对数字…...

实验十 Servlet(一)
实验十 Servlet(一) 【实验目的】 1.了解Servlet运行原理 2.掌握Servlet实现方式 【实验内容】 1、参考课堂例子,客户端通过login.jsp发出登录请求,请求提交到loginServlet处理。如果用户名和密码相同则视为登录成功,…...
doris:聚合模型的导入更新
这篇文档主要介绍 Doris 聚合模型上基于导入的更新。 整行更新 使用 Doris 支持的 Stream Load,Broker Load,Routine Load,Insert Into 等导入方式,往聚合模型(Agg 模型)中进行数据导入时,都…...
Java NIO_非阻塞I/O的实现与优化
1. 引言 1.1 背景介绍 随着互联网应用的快速发展,传统的阻塞I/O模型已经无法满足高并发、高性能的需求。Java NIO(Non-blocking I/O)提供了高效的非阻塞I/O操作,使得开发者能够构建高性能的网络应用和文件处理系统。 1.2 Java NIO的重要性 Java NIO通过非阻塞I/O和多路…...
代码随想录算法训练营Day51 | 101.孤岛的总面积、102.沉没孤岛、103.水流问题、104.建造最大岛屿
文章目录 101.孤岛的总面积思路与重点 102.沉没孤岛思路与重点 103.水流问题思路与重点 104.建造最大岛屿思路与重点 101.孤岛的总面积 题目链接:101.孤岛的总面积讲解链接:代码随想录状态:直接看题解了。 思路与重点 nextx或者nexty越界了…...

Games202Lecture 6 Real-time Environment Mapping
RTRT RTRT(real time ray tracing): path tracingdenoising PRT PRT (Precomputed radiance transfer):离线预计算,运行时快速内积。 预计算(Offline Precomputation): 传输函数(Transfer Function&…...

在 Zemax 中使用布尔对象创建光学光圈
在 Zemax 中,布尔对象用于通过组合或减去较简单的几何形状来创建复杂形状。布尔运算涉及使用集合运算(如并集、交集和减集)来组合或修改对象的几何形状。这允许用户在其设计中为光学元件或机械部件创建更复杂和定制的形状。 本视频中…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...

回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...

使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...

Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...