goroutine源码分析,直击并发底层实现

简介: goroutine源码分析,直击并发底层实现

概述

Go 语言中的 goroutine 是一种轻量级线程, goroutine 之间通过 channel 进行通信。

使用 goroutine 可以方便地编写并发程序。

本文将介绍 goroutine 的使用方法、调度原理、同步处理及实战案例等内容。

主要内容包括

goroutine 简介

goroutine 使用方法

goroutine 调度原理

goroutine 同步处理

避免 goroutine 泄漏和死锁

goroutine 使用技巧

goroutine 实战案例

goroutine 源码分析


 

1、goroutine 简介

goroutine 是 Go 语言提供的轻量级线程,具有以下特点

每个 goroutine 占用内存很小,只有 2KB 左右

调度成本小,可以轻松创建上万个 goroutine

goroutine 通过 channel 通信传递数据

由 Go 运行时(runtime)自动调度执行

goroutine 让并发编程变得更简单高效。


 

2、goroutine 使用方法

使用go语句即可启动一个 goroutine,语法如下


go 函数名(参数列表)

例如



// 开启处理data的goroutinego process(data)

启动时可传递参数,参数需为原值或引用类型,否则参数副本无法修改


func sum(s []int, ch chan int) {  sum := 0  for _, v := range s {    sum += v  }  ch <- sum // 返回结果}
go sum(s, resultCh)

匿名函数可直接启动 goroutine


go func() {  // 使用外部变量  ...}()


 

3、goroutine 调度原理

Go 运行时通过 GMP 模型调度 goroutine

G - goroutine

P - processor,对应 OS 线程

M - machine,关联线程栈等执行上下文

GMP 共同协作完成 goroutine 调度执行,runtime 会根据负载动态分配 P 与 G。

runtime 采用以下技术优化调度:

复用线程,减少创建开销

工作窃取算法动态平衡负载

GOMAXPROCS 参数限制 CPU 使用数


 

4、goroutine 同步处理

1. WaitGroup

WaitGroup 可以等待一组 goroutine 完成


var wg sync.WaitGroup
func process() {  wg.Add(1)   defer wg.Done()     // do work  }
wg.Add(3) // 设置期望goroutine数go process() // 启动多个goroutinewg.Wait() // 等待结束

2. Channel

Channel 可用于 goroutine 间同步


ch := make(chan int) 
go func() {  // 写入channel   ch <- 1 }()
// 从channel读取数据<-ch

可设置标志 channel 协调 goroutine


done := make(chan bool)
go func() {   done <- true}()  
<-done


 

5、避免 goroutine 泄漏和死锁

1. 泄漏

goroutine 可能发生泄漏


func main() {  ch := make(chan int)  go func() {    <-ch // 可能一直阻塞  }()}

解决方法是在 main goroutine 退出前,关闭 channel


close(ch) // 主动关闭channel

2. 死锁

死锁场景


ch := make(chan int)
func main() {  go func() {     ch <- 1 // 阻塞,等待接收  }()
  <-ch // 阻塞,等待发送  }

必须破坏死锁条件才能修复,如修改为单向 channel。


 

6、goroutine 使用技巧

优化 goroutine 使用的技巧

利用缓冲 channel 并发限速

避免过多阻塞与交互

合理设置 GOMAXPROCS

profiling 找出性能瓶颈

使用定时器防止泄漏

请注意不要启动过多阻塞的 goroutine 。


 

7、goroutine 实战案例

1.

func search(query string) {  var wg sync.WaitGroup  for _, site := range sites {    wg.Add(1)    go func(site string) {      defer wg.Done()      search(site, query)    }(site)  }  
  wg.Wait() // 等待结束}

2.

func sort(items []int) {  var wg sync.WaitGroup   ch := make(chan []int) // 分割后子序列 channel
  // 分割为多个子序列  for i := range items {    wg.Add(1)    go func(sub []int) {      defer wg.Done()        sort(sub) // 排序子序列      ch <- sub // 返回排序结果    }(items[i:j])   }
  // 合并排序结果  for range items {    sorted := <-ch    // 添加到最终结果  }
  wg.Wait()}


 

8、goroutine 源码分析

goroutine 的调度实现位于 runtime 下的 proc.go 文件中,主要涉及下面几个结构

g 对象,代表 goroutine ,定义在 proc.go


type g struct {  // goroutine状态   status int  // goroutine运行栈信息  stack stack    // 入口函数等信息  entry stack.entry}

m 对象,代表操作系统线程,在 proc.go


type m struct {  // 绑定的操作系统线程id  tid int   // 调度相关信息  sched sched  //  caches缓存  caches cache   // 关联的p  p p}

p 对象,代表逻辑处理器,在 proc.go


type p struct {  id int  status uint32  // 本地可运行goroutine队列  runq gQueue }

runtime 会使用 g 结构体对象表示 goroutine, g 对象保存 goroutine 状态、调度信息等;


m 代表机器结构,维护与操作系统线程相关资源;

p 代表处理器逻辑单元,绑定特定 m,可运行 g;

runtime 会维护全局 g 队列及 p 的本地可运行 g 队列;

当创建 goroutine 时,会保存 goroutine 入口等信息到 g 对象;

findRunnable 函数会从队列中弹出待执行 g,放入 p 的本地队列;

retake 会尝试从其他 p 的队列窃取 g 来执行。

runtime 通过这些手段管理 goroutine 的调度和执行,实现高效的并发。


 

总结

goroutine 是 Go 语言轻量级线程,可以大规模并发。了解 goroutine 机制,正确使用是 Go 并发编程的关键。

本文从多个维度详细介绍了 goroutine 的工作原理、使用技巧等内容,可以帮助读者深入地理解和运用 goroutine ,编写高效的并发程序。

目录
相关文章
|
存储 Java 编译器
Go函数解密:底层工作原理
Go函数解密:底层工作原理
326 0
|
缓存 NoSQL Java
面试官:如何保证本地缓存的一致性?
面试官:如何保证本地缓存的一致性?
2846 1
|
NoSQL IDE Go
Go 语言源码级调试器 Delve
Go 语言源码级调试器 Delve
372 0
|
7月前
|
数据采集 JSON Go
Go语言实战案例:实现HTTP客户端请求并解析响应
本文是 Go 网络与并发实战系列的第 2 篇,详细介绍如何使用 Go 构建 HTTP 客户端,涵盖请求发送、响应解析、错误处理、Header 与 Body 提取等流程,并通过实战代码演示如何并发请求多个 URL,适合希望掌握 Go 网络编程基础的开发者。
|
NoSQL 关系型数据库 MySQL
MySQL与Redis协同作战:优化百万数据查询的实战经验
【10月更文挑战第13天】 在处理大规模数据集时,传统的关系型数据库如MySQL可能会遇到性能瓶颈。为了提升数据处理的效率,我们可以结合使用MySQL和Redis,利用两者的优势来优化数据查询。本文将分享一次实战经验,探讨如何通过MySQL与Redis的协同工作来优化百万级数据统计。
726 5
|
Java Windows
[main] DEBUG Sigar - no sigar-amd64-winnt.dll in java.library.path org.hyperic.sigar.SigarException:
[main] DEBUG Sigar - no sigar-amd64-winnt.dll in java.library.path org.hyperic.sigar.SigarException:
420 1
|
机器学习/深度学习 NoSQL Go
如何进行Go程序的打包发布
如何进行Go程序的打包发布
390 7
|
缓存 Rust 前端开发
【一起学Rust | 框架篇 | Tauri2.0框架】Tauri2.0环境搭建与项目创建
【一起学Rust | 框架篇 | Tauri2.0框架】Tauri2.0环境搭建与项目创建
2293 0
|
SQL 关系型数据库 MySQL
MySQL 更新1000万条数据和DDL执行时间分析
MySQL 更新1000万条数据和DDL执行时间分析
1024 4
|
机器学习/深度学习 编解码 测试技术
【YOLOv8改进】LSKNet(Large Selective Kernel Network ):空间选择注意力 (论文笔记+引入代码)
YOLO目标检测专栏介绍了YOLO的有效改进和实战应用,包括卷积、主干网络、注意力机制和检测头的创新。提出的新模型LSKNet利用大型选择性核关注遥感场景的先验知识,动态调整感受野,提升目标检测效果。创新点包括LSKblock Attention、大型选择性核网络和适应性感受野调整。LSKNet在多个遥感检测基准上取得最优性能,且结构轻量。此外,文章提供了YOLOv8的LSKNet实现代码。更多详情可查阅相关专栏链接。

热门文章

最新文章