You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

runtime.GOMAXPROCS在显式创建更多协程时失效?如何正确使用runtime.GOMAXPROCS?

关于runtime.GOMAXPROCS的正确使用与并发协程控制误区

兄弟,你这是把GOMAXPROCS的作用和协程并发数控制搞混啦!咱先掰扯清楚这俩的区别,再看你代码里的问题出在哪。

先搞懂runtime.GOMAXPROCS到底管啥

runtime.GOMAXPROCS(n) 设置的是Go调度器能同时使用的操作系统线程数,不是你以为的“同时运行的协程数”。Go的协程(goroutine)是轻量级用户态线程,调度器会把大量协程多路复用到这些OS线程上。哪怕你设置GOMAXPROCS(2),照样能创建成千上万个协程——只是同一时刻最多有2个协程能在OS线程上执行,其他协程会处于等待调度的状态(比如阻塞、休眠或者就绪排队)。

你的代码为啥会启动5个协程?

看你代码里的这段:

var concurrency = 5
waiter := make(chan struct{}, concurrency)
// ...
for i := 0; i < items; i++ {
    wg.Add(1)
    waiter <- struct{}{}
    go worker(i, waiter, &wg)
}

这里的waiter是缓冲为5的通道,意味着你可以连续往里面塞5个空结构体,然后一次性启动5个worker协程。这5个协程都会被创建并进入执行流程——只是因为GOMAXPROCS(2),同一时间最多2个能在CPU上跑,但剩下3个是就绪状态,一旦前面的协程让出CPU(比如time.Sleep的时候),调度器就会把它们调度到空闲的OS线程上运行。所以你看到的“5个协程在运行”,其实是指它们都已经启动了,只是不是同时占用CPU而已。

怎么实现“限制同时运行的协程数”?

如果你的需求是控制并发执行的协程数量(也就是并发度),那GOMAXPROCS不是干这个的,得用信号量模式(带缓冲的通道)来实现。给你改好的代码:

package main

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

func worker(i int, sem chan struct{}, wg *sync.WaitGroup) {
    // 先获取信号量,没有可用的就阻塞等待
    <-sem
    defer func() {
        fmt.Printf("worker %d done\n", i)
        wg.Done()
        // 协程结束后释放信号量,让下一个协程可以启动
        sem <- struct{}{}
    }()
    fmt.Printf("worker %d starting\n", i)
    time.Sleep(time.Second)
}

func main() {
    runtime.GOMAXPROCS(2)
    // 这里设置你想要的并发度,比如2
    const concurrency = 2
    var items = 10
    // 信号量通道的缓冲大小等于并发度
    sem := make(chan struct{}, concurrency)
    // 先把信号量填满,初始化可用的“并发名额”
    for i := 0; i < concurrency; i++ {
        sem <- struct{}{}
    }
    var wg sync.WaitGroup
    for i := 0; i < items; i++ {
        wg.Add(1)
        go worker(i, sem, &wg)
    }
    wg.Wait()
    close(sem)
}

修正点说明:

  • 把原来的waiter改成信号量sem,缓冲大小设为你想要的并发度(比如2)
  • worker协程启动时先获取信号量:<-sem,如果信号量被占满,就会阻塞,直到有其他协程释放信号量
  • 协程结束时释放信号量:sem <- struct{}{},把“并发名额”让出来,让新的协程可以启动
  • 这样就能保证同一时刻最多有concurrency个协程在执行,配合GOMAXPROCS(2),就能达到你预期的“仅运行2个执行单元”的效果

最后总结下

  • GOMAXPROCS控制的是Go调度器使用的OS线程数,主要用来控制CPU核心的利用(比如你有4核CPU,设为4就能最大化利用CPU)
  • 要限制协程的并发执行数,必须用信号量模式(带缓冲的通道)来实现
  • 你原代码的通道缓冲是5,相当于一次放了5个“启动名额”,所以会一次性启动5个协程,这和GOMAXPROCS的设置不冲突——只是它们不会同时占用CPU而已

内容的提问来源于stack exchange,提问作者NonoPa Naka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 17:22:31