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 ,编写高效的并发程序。

目录
相关文章
|
6天前
|
数据处理
多线程与并发编程【线程对象锁、死锁及解决方案、线程并发协作、生产者与消费者模式】(四)-全面详解(学习总结---从入门到深化)
多线程与并发编程【线程对象锁、死锁及解决方案、线程并发协作、生产者与消费者模式】(四)-全面详解(学习总结---从入门到深化)
44 1
|
6天前
|
存储 算法 Java
12张图一次性搞懂高性能并发容器ConcurrentLinkedQueue
12张图一次性搞懂高性能并发容器ConcurrentLinkedQueue
|
6天前
|
存储 监控 算法
深入探究Java线程池:提升并发性能的利器
深入探究Java线程池:提升并发性能的利器
155 0
|
存储 Java
并发编程(十)线程池核心原理与源码剖析
并发编程(十)线程池核心原理与源码剖析
83 0
|
安全 算法 Java
【并发编程技术】「技术辩证分析」在并发编程模式下进行线程安全以及活跃性问题简析
【并发编程技术】「技术辩证分析」在并发编程模式下进行线程安全以及活跃性问题简析
56 0
【并发编程技术】「技术辩证分析」在并发编程模式下进行线程安全以及活跃性问题简析
阿里巴巴面试题- - -多线程&并发篇(二十三)
阿里巴巴面试题- - -多线程&并发篇(二十三)
阿里巴巴面试题- - -多线程&并发篇(二十六)
阿里巴巴面试题- - -多线程&并发篇(二十六)
阿里巴巴面试题- - -多线程&并发篇(二十五)
阿里巴巴面试题- - -多线程&并发篇(二十五)
阿里巴巴面试题- - -多线程&并发篇(二十七)
阿里巴巴面试题- - -多线程&并发篇(二十七)
|
缓存 Java
阿里巴巴面试题- - -多线程&并发篇(二十九)
阿里巴巴面试题- - -多线程&并发篇(二十九)