惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Microsoft Security Blog
Microsoft Security Blog
WordPress大学
WordPress大学
S
SegmentFault 最新的问题
爱范儿
爱范儿
B
Blog RSS Feed
Last Week in AI
Last Week in AI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Blog — PlanetScale
Blog — PlanetScale
Vercel News
Vercel News
Jina AI
Jina AI
aimingoo的专栏
aimingoo的专栏
I
Intezer
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Attack and Defense Labs
Attack and Defense Labs
The GitHub Blog
The GitHub Blog
小众软件
小众软件
AI
AI
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
N
News and Events Feed by Topic
腾讯CDC
D
Docker
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
罗磊的独立博客
人人都是产品经理
人人都是产品经理
W
WeLiveSecurity
N
News and Events Feed by Topic
Security Archives - TechRepublic
Security Archives - TechRepublic
C
Check Point Blog
Webroot Blog
Webroot Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
H
Help Net Security
Recorded Future
Recorded Future
H
Hacker News: Front Page
T
Troy Hunt's Blog
V
V2EX
Forbes - Security
Forbes - Security
Stack Overflow Blog
Stack Overflow Blog
The Register - Security
The Register - Security
P
Palo Alto Networks Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 叶小钗
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Security Affairs
The Hacker News
The Hacker News
Simon Willison's Weblog
Simon Willison's Weblog
博客园 - 三生石上(FineUI控件)
B
Blog
Apple Machine Learning Research
Apple Machine Learning Research
C
Cyber Attacks, Cyber Crime and Cyber Security
D
DataBreaches.Net

郑文峰的博客

使用dify对接飞书多维表格 使用n8n对接飞书多维表格 服务启动时出现 OOM 一次服务升级时pg表DDL执行超时失败 Go语言高效IO缓冲技术详解 Go语言延迟初始化(Lazy Initialization)最佳实践 Go语言字符串拼接性能对比与优化指南 Go语言结构体内存对齐完全指南 Go语言空结构体:零内存消耗的高效编程 Go语言堆栈分配与逃逸分析深度解析 Go语言原子操作完全指南 Go语言内存预分配完全指南 Go语言不可变数据共享:无锁并发编程实践 Go语言零拷贝技术完全指南 Go语言遍历性能深度解析:从原理到优化实践 Go语言Interface Boxing原理与性能优化指南 使用etcd分布式锁导致的协程泄露与死锁问题 基于pre-commit的Python代码规范落地实践 初识 MCP Server pulsar阻塞导致logstash无法接入日志 django-prometheus使用及源码分析 kube-proxy源码分析 kubernetes service如何通过iptables转发 tcp缓存引起的日志丢失 django-apschedule定时任务异常停止 理解calico容器网络通信方案原理 理解flannel的三种容器网络方案原理 理解Linux IPIP隧道 理解VXLAN网络 理解Linux TunTap设备 快速了解iptables kafka中listener和advertised.listeners的作用 django rest_framework 分页 django后端服务、logstash和flink接入VictoriaMetrics指标监控 python中import原理 docker容器单机网络 手动实现docker容器bridge网络模型 mysql之MVCC原理 mysql之日志 使用java开发logstash的filter插件 使用python实现单例模式的三种方式 redis之缓存 redis之分片集群 redis之哨兵机制 redis之主从库同步 redis之持久化 redis之五种基本数据类型 go中如何处理error pod中将代码与运行环境分离 ddt源码分析 python装饰器的使用方法 读书笔记:如何阅读一本书 使用ddt实现unittest的参数化测试 使用kubeadm安装k8s 优化gin表单的错误提示信息 gin中validator模块的源码分析 go简单使用grpc python简单使用grpc k8s之PV、PVC和StorageClass k8s之StatefulSet k8s之DaemonSet k8s之Job和CronJob k8s之ConfigMap和Secret k8s之Service k8s之Pod k8s之Deployment 容器的本质 docker容器 python迭代器与生成器 python元编程 python垃圾回收机制 python上下文管理器 django rest_framework使用jwt django rest_framework异常处理 django rest_framework 自定义文档 django压缩文件下载 django rest_framework使用pytest单元测试 django restframework choice 自定义输出数据 django Filtering 使用 django viewset 和 Router 配合使用时报的错 django model的序列化 django中使用AbStractUser django.core.exceptions.ImproperlyConfigured Application labels aren't unique, duplicates users django 中 media配置 django 外键引用自身和on_delete参数 django 警告 while time zone support is active Flask使用flask_socketio实现websocket flask结合mongo tornado 文件上传 tornado 使用jwt完成用户异步认证 tornado 用户密码 bcrypt加密 tornado 结合wtforms使用表单操作 tornado finish和write区别 tornado 使用peewee-async 完成异步orm数据库操作 pyspark streaming简介 和 消费 kafka示例 使用hue创建ozzie的pyspark action workflow count的性能优化 django rest_framework Authentication django celery 结合使用 网站
Go协程池深度解析:原理、实现与最佳实践
zhengwenfeng · 2025-06-10 · via 郑文峰的博客

# 为什么需要协程池?

goroutine 虽然是轻量级的并发模型,但是协程也是有栈空间的,并且有上下文切换的开销,当协程数量增加时,性能可能会急剧的下降,甚至导致程序崩溃。

而协程池限制 gorotinue 的数量,并从共享的任务队列中提取任务执行,从而让 goroutine可控,不会超过其处理的能力,保证服务的稳定性。

17494714259921749471425027.png

# 怎么使用协程池?

提前创建 5 个worker,再创建一个 jobs channel 用于传递任务,最后在不断地将任务生产到队列中,worker获取到任务后执行,最后将 jobs close掉,协程也就都退出了,最后程序退出。

func worker(id int, jobs <-chan int, results chan<- [32]byte) {
    for j := range jobs {
        results <- doWork(j)
    }
}

func doWork(n int) [32]byte {
    data := []byte(fmt.Sprintf("payload-%d", n))
    return sha256.Sum256(data) //
}

func main() {
    jobs := make(chan int, 100)
    results := make(chan [32]byte, 100)

    for w := 1; w <= 5; w++ {
        go worker(w, jobs, results)
    }

    for j := 1; j <= 10; j++ {
        jobs <- j
    }
    close(jobs)

    for a := 1; a <= 10; a++ {
        <-results
    }

    fmt.Println("ending")
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

可以自己实现,也可以直接利用已经实现好的开源协程池。

https://github.com/Jeffail/tunny

https://github.com/panjf2000/ants

https://github.com/bytedance/gopkg/tree/main/util/gopool

# Worker Pools 该设置成多大?

池中的协程最优的协程数量和 CPU 核数密切相关。可以使用 runtime.NumCPU()runtime.GOMAXPROCS(0) 来获取 CPU 核数。

对于 CPU 密集型任务,通常工作协程数少于或等于逻辑 CPU 核数,可以让 CPU 利用率达到最大。而对于 IO 密集型任务,可以让工作协程数大于CPU核数,以为遇到 IO 会进行阻塞,也就是工作协程大部分时间处于阻塞状态。

# BenchMark

使用协程池和不使用协程池处理 10000 个任务的 BenchMark 比较

const (
    numJobs     = 10000
    workerCount = 10
)

func doWork(n int) [32]byte {
    data := []byte(fmt.Sprintf("payload-%d", n))
    return sha256.Sum256(data)
}

func BenchmarkUnboundedGoroutines(b *testing.B) {
    for range b.N {
        var wg sync.WaitGroup
        wg.Add(numJobs)

        for j := 0; j < numJobs; j++ {
            go func(job int) {
                _ = doWork(job)
                wg.Done()
            }(j)
        }
        wg.Wait()
    }
}

func worker(jobs <-chan int, wg *sync.WaitGroup) {
    for job := range jobs {
        _ = doWork(job)
        wg.Done()
    }
}

func BenchmarkWorkerPool(b *testing.B) {
    for range b.N {
        var wg sync.WaitGroup
        wg.Add(numJobs)

        jobs := make(chan int, numJobs)
        for w := 0; w < workerCount; w++ {
            go worker(jobs, &wg)
        }

        for j := 0; j < numJobs; j++ {
            jobs <- j
        }

        close(jobs)
        wg.Wait()
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

运行结果如下,使用协程池使用的资源更少,更快的完成工作。

 $ go test -bench=. -benchmem .  
goos: darwin
goarch: arm64
pkg: main/demo
cpu: Apple M4 Pro
BenchmarkUnboundedGoroutines-12              486           2501263 ns/op          639942 B/op      39754 allocs/op
BenchmarkWorkerPool-12                       776           1540660 ns/op          320554 B/op      19758 allocs/op
PASS
ok      main/demo       3.343s

1
2
3
4
5
6
7
8
9

# 什么时候使用 Worker Pools?

  • 有大量或无限制的任务流处理。
  • 为了程序的稳定性,限制并行操作的数量。

# 什么时候该避免使用 Worker pools?

  • 对任务的延迟非常敏感,需要立即执行。
  • 低负载的情况下,使用协程池反而增高了成本。
  • 工作量较小并且是有限的。