Go Academy · 课时

限制 Goroutine

控制并发量

第 3 / 4 课13 个步骤

限制 Goroutine 是 CoddyKit 上的免费 Go Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Go Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Go Academy 课程共包含 4 节课。

并发限流

有时您限制的不是每秒请求数,而是同时运行的 goroutine 数量。无论请求的时间安排如何,这都能限制资源所承受的同时负载。

  • 限制并行度
  • 保护连接池
  • 防止内存峰值

信号量通道

带缓冲的通道可以充当计数信号量。它的容量等于最大并发数。获取许可时发送令牌,释放许可时接收令牌。

sem := make(chan struct{}, 3)

获取与释放

开始工作前,请向通道发送数据以获取一个槽位(通道已满时会阻塞)。完成后,请接收数据来释放槽位,为另一个 goroutine 腾出空间。

sem <- struct{}{}        // acquire
// do work
<-sem                    // release

为什么使用 struct{}

空结构体 struct{}{} 占用零字节。将它用作通道元素后,信号量就只传递信号,不会浪费内存。

循环限流

为每个任务启动一个 goroutine,但让每个 goroutine 都受信号量控制。最多只有 cap 个 goroutine 会同时执行实际工作,其余 goroutine 会在获取许可处阻塞。

for _, task := range tasks {
    sem <- struct{}{}
    go func(t Task) {
        defer func() { <-sem }()
        process(t)
    }(t)
}

与 WaitGroup 配合

信号量可以限制并发数,但不会告诉您所有任务何时完成。请将它与 sync.WaitGroup 结合,以等待所有任务结束。

var wg sync.WaitGroup
wg.Add(1)
go func() {
    defer wg.Done()
    defer func() { <-sem }()
    process(t)
}()

在 defer 中释放

请始终在 defer 中释放槽位,这样即使工作发生恐慌或提前返回,槽位也能被释放。忘记释放会造成槽位泄漏,最终使新的获取者发生死锁。

完整的限流运行示例

此程序运行八个任务,同时最多运行两个任务,并打印开始和结束信息来展示并发上限的实际效果。

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    sem := make(chan struct{}, 2)
    var wg sync.WaitGroup
    for i := 0; i < 8; i++ {
        wg.Add(1)
        sem <- struct{}{}
        go func(id int) {
            defer wg.Done()
            defer func() { <-sem }()
            fmt.Println("start", id)
            time.Sleep(20 * time.Millisecond)
            fmt.Println("end", id)
        }(i)
    }
    wg.Wait()
    fmt.Println("done")
}

errgroup 与 SetLimit

golang.org/x/sync/errgroup 包提供了 SetLimit,可以限制一组任务的并发数,并传递第一个错误。在许多情况下,它是更简洁的替代方案,但它属于外部包。

限流与工作池

工作池会复用一组固定的 goroutine,让它们从队列中获取任务。信号量限流则为每个任务启动一个 goroutine,但限制处于活动状态的 goroutine 数量。对于数量极大的任务,工作池更加精简;信号量则更容易直接添加到现有代码中。

选择限制值

对于数据库调用,请将限制值设置为不高于连接池大小。对于出站 HTTP 请求,请与主机的连接限制相匹配。限制过高会耗尽资源,限制过低则会浪费容量。

快速检查

请测试您对限流的了解。

总结

您学会了 goroutine 限流:

  • 带缓冲的通道可以充当计数信号量
  • 通过发送来获取许可,并在 defer 中通过接收来释放许可
  • struct{} 元素不占用任何字节
  • 与 WaitGroup 配合以等待任务完成
  • 根据受保护资源的情况设置限制值
免费开始

用 AI 导师学习 Go — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
51
课程
203

常见问题解答

「限制 Goroutine」课时是免费的吗?

是的 — 「限制 Goroutine」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Go Academy 课程的其余内容,请升级到 CoddyKit PRO。 Go Academy 课程共包含 4 节课。

「限制 Goroutine」这节课中我会学到什么?

控制并发量 你通过在浏览器中直接运行的动手代码来练习 Go Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Go Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Go Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「限制 Goroutine」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Go Academy 课中编写并运行代码吗?

能。每节 Go Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 令牌桶概念
  2. golang.org/x/time/rate
  3. 限制 Goroutine
  4. 退避与重试
← 返回 Go Academy