深入浅出 Go 语言:协程(Goroutine)详解
深入浅出 Go 语言:协程(Goroutine)详解
引言
Go 语言的协程(goroutine)是其并发模型的核心特性之一。协程允许你轻松地编写并发代码,而不需要复杂的线程管理和锁机制。通过协程,你可以同时执行多个任务,并且这些任务可以共享相同的地址空间,从而简化了内存管理和数据共享。
本文将深入浅出地介绍 Go 语言中的协程编程,涵盖协程的基本概念、如何启动和管理协程、通道(channel)的使用以及常见的并发模式。
1. 协程的基本概念
1.1 什么是协程?
协程是一种轻量级的线程,它由 Go 运行时自动调度和管理。与传统的操作系统线程不同,协程的创建和切换开销非常小,因此可以在一个程序中创建成千上万个协程,而不会对性能造成显著影响。
在 Go 中,协程通过 go 关键字启动。任何函数都可以作为协程运行,只需在其调用前加上 go 关键字即可。
1.1.1 启动协程
启动协程的基本语法如下:
go 函数名(参数列表)
例如,启动一个简单的协程:
func sayHello() {fmt.Println("Hello, World!")
}func main() {go sayHello()time.Sleep(time.Second) // 确保主程序等待协程完成
}
在这个例子中,sayHello 函数作为一个协程启动。由于协程是异步执行的,主程序可能会在协程完成之前结束。为了确保协程有足够的时间执行,我们在主程序中添加了一个 time.Sleep,以等待一段时间。
1.2 协程的特点
- 轻量级:协程的创建和切换开销非常小,可以在一个程序中创建大量协程。
- 自动调度:协程由 Go 运行时自动调度,开发者不需要手动管理线程的创建和销毁。
- 共享内存:协程之间可以共享相同的地址空间,简化了内存管理和数据共享。
- 非阻塞:协程之间的通信和同步是非阻塞的,避免了传统线程中的锁竞争问题。
1.3 协程与线程的区别
- 线程:由操作系统管理,创建和切换开销较大,适用于需要高性能和复杂调度的场景。
- 协程:由 Go 运行时管理,创建和切换开销较小,适用于高并发场景,尤其是 I/O 密集型任务。
2. 协程的管理
2.1 协程的生命周期
协程的生命周期由 Go 运行时自动管理,开发者不需要显式地创建或销毁协程。然而,在某些情况下,我们仍然需要控制协程的执行,以确保程序的正确性和性能。
2.1.1 使用 WaitGroup 等待协程完成
在多协程场景中,主程序通常需要等待所有协程完成后再退出。sync.WaitGroup 是 Go 提供的一个工具,用于等待一组协程完成。
简单示例
以下是一个使用 WaitGroup 等待协程完成的示例:
package mainimport ("fmt""sync""time"
)func worker(id int, wg *sync.WaitGroup) {defer wg.Done() // 在函数返回时调用 Donefmt.Printf("Worker %d starting
", id)time.Sleep(time.Second)fmt.Printf("Worker %d done
", id)
}func main() {var wg sync.WaitGroupfor i := 1; i <= 5; i++ {wg.Add(1) // 每启动一个协程,增加计数器go worker(i, &wg)}wg.Wait() // 等待所有协程完成fmt.Println("All workers done")
}
在这个例子中,WaitGroup 用于跟踪启动的协程数量,并在所有协程完成后通知主程序。wg.Add(1) 用于增加计数器,wg.Done() 用于减少计数器,wg.Wait() 用于阻塞主程序,直到所有协程完成。
2.1.2 使用 context 控制协程的取消
在某些情况下,我们可能需要提前取消协程的执行。context 包提供了上下文管理功能,允许你在协程之间传递取消信号。
简单示例
以下是一个使用 context 控制协程取消的示例:
package mainimport ("context""fmt""time"
)func worker(ctx context.Context, id int) {for {select {case <-ctx.Done():fmt.Printf("Worker %d canceled
", id)returndefault:fmt.Printf("Worker %d working
", id)time.Sleep(500 * time.Millisecond)}}
}func main() {ctx, cancel := context.WithCancel(context.Background())for i := 1; i <= 3; i++ {go worker(ctx, i)}time.Sleep(2 * time.Second)cancel() // 发送取消信号time.Sleep(1 * time.Second) // 确保协程有时间处理取消信号
}
在这个例子中,context.WithCancel 创建了一个带有取消功能的上下文。当调用 cancel() 时,所有监听该上下文的协程都会收到取消信号并退出。
3. 通道(Channel)
通道是 Go 语言中用于协程之间通信的机制。通过通道,协程可以安全地发送和接收数据,而不需要使用锁或其他同步原语。
3.1 通道的基本用法
创建通道的基本语法如下:
ch := make(chan 类型)
例如,创建一个整数类型的通道:
ch := make(chan int)
3.1.1 发送和接收数据
发送数据到通道的语法为 ch <- value,接收数据的语法为 value := <-ch。
简单示例
以下是一个使用通道进行协程间通信的示例:
package mainimport ("fmt"
)func send(ch chan<- int, value int) {ch <- value
}func receive(ch <-chan int) {value := <-chfmt.Println("Received:", value)
}func main() {ch := make(chan int)go send(ch, 42)receive(ch)
}
在这个例子中,send 协程向通道发送数据,receive 协程从通道接收数据。注意,通道的方向可以通过箭头符号指定,chan<- 表示只写通道,<-chan 表示只读通道。
3.2 无缓冲通道与带缓冲通道
- 无缓冲通道:默认情况下,通道是无缓冲的。发送和接收操作必须同时发生,否则会导致阻塞。
- 带缓冲通道:通过指定缓冲区大小,可以创建带缓冲的通道。发送操作不会立即阻塞,直到缓冲区满为止;接收操作也不会立即阻塞,直到缓冲区为空为止。
带缓冲通道示例
以下是一个使用带缓冲通道的示例:
package mainimport ("fmt"
)func main() {ch := make(chan int, 2) // 创建带缓冲的通道ch <- 1ch <- 2fmt.Println(<-ch) // 输出: 1fmt.Println(<-ch) // 输出: 2
}
在这个例子中,make(chan int, 2) 创建了一个容量为 2 的带缓冲通道。我们可以连续发送两个值而不阻塞,直到缓冲区满为止。
3.3 选择器(Select)
select 语句用于在多个通道操作之间进行选择。它可以监听多个通道的发送和接收操作,并根据最先准备好的操作执行相应的代码块。
简单示例
以下是一个使用 select 语句的示例:
package mainimport ("fmt""time"
)func main() {ch1 := make(chan string)ch2 := make(chan string)go func() {time.Sleep(2 * time.Second)ch1 <- "Hello from ch1"}()go func() {time.Sleep(1 * time.Second)ch2 <- "Hello from ch2"}()select {case msg1 := <-ch1:fmt.Println(msg1)case msg2 := <-ch2:fmt.Println(msg2)}
}
在这个例子中,select 语句监听了两个通道 ch1 和 ch2。由于 ch2 的协程先完成,因此 select 会优先处理 ch2 的消息。
4. 常见的并发模式
4.1 工作者池模式
工作者池模式是一种常见的并发模式,适用于需要处理大量任务的场景。通过创建一个固定数量的协程池,可以有效地复用协程,避免频繁创建和销毁协程带来的开销。
简单示例
以下是一个实现工作者池模式的示例:
package mainimport ("fmt""sync"
)type Task struct {ID intData string
}func worker(tasks <-chan Task, results chan<- string, wg *sync.WaitGroup) {defer wg.Done()for task := range tasks {result := fmt.Sprintf("Processed task %d with data: %s", task.ID, task.Data)results <- result}
}func main() {numWorkers := 3numTasks := 10tasks := make(chan Task, numTasks)results := make(chan string, numTasks)var wg sync.WaitGroup// 启动工作者协程for i := 0; i < numWorkers; i++ {wg.Add(1)go worker(tasks, results, &wg)}// 发送任务for i := 1; i <= numTasks; i++ {tasks <- Task{ID: i, Data: fmt.Sprintf("Task %d", i)}}close(tasks)// 收集结果go func() {wg.Wait()close(results)}()for result := range results {fmt.Println(result)}
}
在这个例子中,我们创建了一个包含 3 个协程的工作池,并向其发送 10 个任务。每个协程从 tasks 通道中获取任务并处理,处理结果通过 results 通道返回。sync.WaitGroup 用于等待所有协程完成。
4.2 生产者-消费者模式
生产者-消费者模式是一种经典的并发模式,适用于需要在多个协程之间共享数据的场景。生产者负责生成数据并将其放入通道,消费者负责从通道中取出数据并进行处理。
简单示例
以下是一个实现生产者-消费者模式的示例:
package mainimport ("fmt""sync"
)func producer(ch chan<- int, wg *sync.WaitGroup) {defer wg.Done()for i := 1; i <= 5; i++ {ch <- ifmt.Printf("Produced: %d
", i)}
}func consumer(ch <-chan int, wg *sync.WaitGroup) {defer wg.Done()for i := range ch {fmt.Printf("Consumed: %d
", i)}
}func main() {ch := make(chan int, 5)var wg sync.WaitGroupwg.Add(1)go producer(ch, &wg)wg.Add(1)go consumer(ch, &wg)wg.Wait()close(ch)
}
在这个例子中,producer 协程负责生成数据并将其放入通道,consumer 协程负责从通道中取出数据并进行处理。sync.WaitGroup 用于等待生产者和消费者完成。
5. 总结
通过本文的学习,你已经掌握了 Go 语言中协程编程的基本概念和使用方法。协程允许你轻松地编写并发代码,而不需要复杂的线程管理和锁机制。我们介绍了如何启动和管理协程、通道的使用以及常见的并发模式。
参考资料
- Go 官方文档 - 并发
- Go 语言中文网 - 协程
- Go 语言官方博客 - 协程

相关文章:
深入浅出 Go 语言:协程(Goroutine)详解
深入浅出 Go 语言:协程(Goroutine)详解 引言 Go 语言的协程(goroutine)是其并发模型的核心特性之一。协程允许你轻松地编写并发代码,而不需要复杂的线程管理和锁机制。通过协程,你可以同时执行多个任务,并…...
vLLM代码推理Qwen2-VL多模态
由于近期代码微调以及测试都是在远程服务器上,因此LLamafactory-cli webui 以及vLLM的ui均无法使用,因此不断寻求解决方案,我提供一个解决方案,LLamafactory微调完成的模型需要合并为一个完整模型后再使用vLLM进行代码推理测试微调…...
DNS云解析有什么独特之处?
在数字化浪潮中,每一次网页点击、视频加载或在线交易背后,都依赖着域名系统(DNS)的高效运转。传统DNS架构的局限性(如单点故障、延迟高、安全脆弱)在云计算时代被彻底颠覆,DNS云解析作为新一代解…...
视频流畅播放相关因素
视频播放的流畅度是一个综合性问题,涉及从视频文件本身到硬件性能、网络环境、软件优化等多个环节。以下是影响流畅度的关键因素及优化建议: 一、视频文件本身 1. 分辨率与帧率 1.问题:高分辨率(如4K)或高帧率&#…...
Python实现一个类似MybatisPlus的简易SQL注解
文章目录 前言实现思路定义一个类然后开始手撸这个微型框架根据字符串获取到所定义的DTO类构建返回结果装饰器解析字符串,获得变量SQL字符串拼接 使用装饰器 前言 在实际开发中,根据业务拼接SQL所需要考虑的内容太多了。于是,有没有一种办法…...
linux一些使用技巧
linux一些使用技巧 文件名称和路径的提取切换用户执行当前脚本一行演示单引号与双引号的使用curl命令仅输出响应头信息,不输出body体文件名称和路径的提取 文件路径为 /tmp/tkgup/test.sh 方式获取文件名获取文件路径获取文件全路径方式一basename ${file}dirname ${file}real…...
小模型和小数据可以实现AGI吗
小模型和小数据很难实现真正的 通用人工智能(AGI, Artificial General Intelligence),但在特定任务或受限环境下,可以通过高效的算法和优化方法实现“近似 AGI” 的能力。 1. 为什么小模型小数据难以实现 AGI? AGI 需…...
io学习----->文件io
思维导图: 一.文件io的概念 文件IO:指程序和文件系统之间的数据交互 特点: 1.不存在缓冲区,访问速度慢 2.不可以移植,依赖于操作系统 3.可以访问不同的文件类型(软连接,块设备等) 4.文件IO属于系统调…...
kubernetes介绍
文章目录 kubernetes概述kubernetes组件kubernetes概念 kubernetes概述 kubernetes,是一个全新的基于容器技术的分布式架构领先方案,是Google开源的的容器编排工具。 kubernetes的本质是一组服务器集群,它可以在集群的每个节点上运行特定…...
如何高效准备PostgreSQL认证考试?
高效准备 PostgreSQL 中级认证考试,可从知识储备、技能提升、模拟考试等方面入手,以下是具体建议: 深入学习理论知识 系统学习核心知识:依据考试大纲,对 PostgreSQL 的体系结构、数据类型、SQL 语言、事务处理、存储过…...
如何使用Briefing打造私有视频会议系统结合内网穿透异地远程连接
文章目录 前言1.关于briefing2.本地部署briefing3.使用briefing4.cpolar内网穿透工具安装5.创建远程连接公网地址6.固定briefing公网地址 前言 在这个‘云’字当道的时代,远程办公、异地恋已经成了生活常态。视频聊天自然也就成了日常操作。但一不小心,…...
XHR请求解密:抓取动态生成数据的方法
在如今动态页面大行其道的时代,传统的静态页面爬虫已无法满足数据采集需求。尤其是在目标网站通过XHR(XMLHttpRequest)动态加载数据的情况下,如何精准解密XHR请求、捕获动态生成的数据成为关键技术难题。本文将深入剖析XHR请求解密…...
坐标变换介绍与机器人九点标定的原理
【备注】本文的C#代码在下面链接中可以下载:Opencv的C#九点标定代码资源-CSDN文库 https://download.csdn.net/download/qq_34047402/90452336 一、坐标变换的介绍 1.绕原点旋转的坐标变换 一个点(x,y)绕原点旋转u度,其旋转后的坐标(x1,y1)如何计算? 2.绕任意点的坐标变…...
串口调试助手Alien v5.198新版发布
v5.198 更改点: 1.增加USB打印机支持 2.支持特殊波特率/自定义波特率 3.支持窗口透明调整 4.支持接收框文本左/中/右对齐,粗体字,自动换行 5.支持接收时间戳 6.HEX接收自动换行 7.支持文本颜色主题 8.支持文本字体修改 9.增加菜单/增状态栏显示当前接口 下载 alien_v5.198.7z …...
解锁Android RemoteViews:跨进程UI更新的奥秘
一、RemoteViews 简介 在 Android 开发的广阔领域中,RemoteViews 是一个独特且重要的概念,它为开发者提供了一种在其他进程中显示视图结构的有效方式。从本质上讲,RemoteViews 并非传统意义上在当前应用进程内直接渲染和操作的 View…...
编译可以在Android手机上运行的ffmpeg程序
下载代码 git clone gitgithub.com:FFmpeg/FFmpeg.git git checkout n7.0建立build目录 mkdir build cd build创建build.sh脚本 vim build.sh这段脚本的主要功能是配置和编译 FFmpeg,使其能够在 Android 平台上运行,通过设置不同的架构和 API 级别&am…...
Verilog学习方法—基础入门篇(一)
前言: 在FPGA开发中,Verilog HDL(硬件描述语言)是工程师必须掌握的一项基础技能。它不仅用于描述数字电路,还广泛应用于FPGA的逻辑设计与验证。对于初学者来说,掌握Verilog的核心概念和基本语法࿰…...
本地jar包添加到 maven
进入到 你的 maven bin文件夹下 执行cmd ,然后执行命令 mvn install:install-file -Dfilepath/to/your/artifact.jar -DgroupIdyour.group.id -DartifactIdyour-artifact-id -Dversion1.0 -Dpackagingjar 替换path/to/your/artifact.jar为你的JAR文件路径…...
C# Unity 唐老狮 No.6 模拟面试题
本文章不作任何商业用途 仅作学习与交流 安利唐老狮与其他老师合作的网站,内有大量免费资源和优质付费资源,我入门就是看唐老师的课程 打好坚实的基础非常非常重要: 全部 - 游习堂 - 唐老狮创立的游戏开发在线学习平台 - Powered By EduSoho 如果你发现了文章内特殊的字体格式,…...
项目工坊 | Python驱动淘宝信息爬虫
目录 前言 1 完整代码 2 代码解读 2.1 导入模块 2.2 定义 TaoBao 类 2.3 search_infor_price_from_web 方法 2.3.1 获取下载路径 2.3.2 设置浏览器选项 2.3.3 反爬虫处理 2.3.4 启动浏览器 2.3.5 修改浏览器属性 2.3.6 设置下载行为 2.3.7 打开淘宝登录页面 2.3.…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
