Golang的Work Stealing机制

Go的运行时系统使用了一种名为Work Stealing(工作窃取)的调度策略来分配Goroutine到可用线程(称为M,即Machine)上执行。这样可以最大化CPU使用率,减少任务调度的开销。在这种机制下,任务队列和调度器通过动态平衡负载来提高并发性能和吞吐量。

Go的调度器使用了P(Processor)与M和Goroutine进行交互。每个P都维护了一个本地的Goroutine队列,新创建的Goroutine首先会被放入创建它的P的本地队列中。在这个系统中,P可以看作是可调度Goroutine的数量,每个P都可以关联一个M来执行Goroutine。

基本工作原理

  • 任务队列:每个工作线程(或goroutine)都有自己的双端队列(deque),用于存储任务。当一个线程生成新任务时,它会将任务放入自己的队列。这种队列就是上述所讲的P处理器。

  • 执行任务:M首先从自己对应P中获取任务并执行。如果它的任务队列为空,它就会尝试从其他线程的任务队列P中窃取任务。

  • 窃取任务:当一个M发现自己的任务队列P为空时,它会随机选择其他M的任务队列P,从队列的另一端窃取任务。这样可以避免竞争,因为线程对自己的任务队列使用一端,而其他线程只能从另一端窃取任务。

  • 负载均衡:通过这种机制,系统能够动态地平衡负载。如果某个线程的任务较多,其他空闲线程可以帮助处理这些任务,从而避免某些线程过载而其他线程空闲的情况。

  • 全局队列:如果所有本地队列P都为空,调度器会从全局队列中获取任务,全局队列存储的是所有P都无法处理的goroutine。

以下是一个简化的示意图,展示了P, M和Goroutine的交互:

复制代码
     P1       P2       P3
     |        |        |
     v        v        v
    [G1,G2] [G3]   [G4,G5,G6] 
     ^        ^        ^
     |        |        |
     M1       M2       M3

在这个图中,我们有3个P(P1、P2和P3),每个P都有一个本地的Goroutine队列。M1、M2和M3是3个线程,每个线程都关联了一个P,并且从其队列中取出Goroutine来执行。当M1完成了G1后,它会从P1的队列中取出G2来执行。如果P1的队列为空,M1就会尝试从P2或P3的队列中"窃取"一个Goroutine。

优点

  1. 高效利用资源:通过动态平衡负载,确保所有线程尽可能地保持忙碌状态,提高CPU利用率。

  2. 减少竞争:窃取任务时,只访问其他线程队列的一端,减少了竞争和锁的使用。

  3. 灵活性:能够自适应负载变化,当任务量不均时,自动进行负载均衡。

最后给大家推荐一个LinuxC/C++高级架构系统教程的学习资源与课程,可以帮助你有方向、更细致地学习C/C++后端开发,具体内容请见 https://xxetb.xetslk.com/s/1o04uB

相关推荐
李迟3 分钟前
Golang实践录:工程框架实践:搭建工程模板
开发语言·后端·golang
FfHUCisI2 小时前
Golang 函数调用的瞬间:栈帧、拷贝栈与逃逸分析
开发语言·后端·golang
王的宝库2 小时前
Gin 框架实战
golang·gin
techdashen19 小时前
Go Map 详解:键值对实际上是如何存储的
开发语言·后端·golang
ttwuai1 天前
Go 后台初始化 MySQL 脚本失败怎么办?先查 DDL 还是生成配置
开发语言·mysql·golang
名字还没想好☜1 天前
Go 的 TCP 粘包与拆包:用长度前缀协议 + bufio 正确读消息
后端·tcp/ip·golang·go·php
王的宝库1 天前
Go 项目结构:从单文件到标准工程布局
开发语言·后端·golang
花酒锄作田2 天前
Go - Gin中使用sessions
golang
不甘先生2 天前
Go 中 type、方法与指针接收者:从 str_name.Name() 看懂 Go 的类型系统
开发语言·后端·golang
Casbin开源社区2 天前
一个面板管住 29 个 AI 编程 Agent:Casbin Gateway 的配置统一、协议互转、用量统计与权限管控
人工智能·golang·开源·gateway·casbin