Go语言中Goroutine CPU核心设置与调度原理

白榆
白榆
发布时间:2026-10-15 | 5人浏览过
原创

在Go语言开发中,合理配置并发执行的物理核心数对性能至关重要。本文将演示如何使用runtime包中的GOMAXPROCS函数动态或静态地限制/设置程序占用的CPU核数,并通过实验对比不同核数设置下的CPU资源消耗情况。

设置并发运行核数

Go 1.5之后默认将GOMAXPROCS设置为等于CPU逻辑核心数的数量,但在需要精细化控制并发度时,开发者仍需手动干预。这主要涉及两个关键API:

  • runtime.NumCPU():获取当前操作系统提供的可用CPU逻辑核心总数。
  • runtime.GOMAXPROCS(n int):设置运行时允许同时执行(抢占式调度)的代码的最大CPU数量。注意参数不能为负数且不能超过系统核心数。

第1步:编写模拟高负载并发程序

为了直观观察核心数对CPU的影响,我们构建一个持续自增的死循环函数doSomething()作为工作负载。

func doSomething() {
    var i int
    for {
        i++
    }
}

func main() {

    cpu := runtime.NumCPU()
    fmt.Println("本机cpu核数:", cpu)

    fmt.Println("输入模拟的cpu数:")
    reader := bufio.NewReader(os.Stdin)
    buf, _ := reader.ReadBytes('\n')
    cmdStr := (string)(buf)
    num, err := strconv.Atoi(strings.Trim(cmdStr, "\r\n"))

    if err != nil || num > cpu || num <= 0 {
        fmt.Println("输入的数字不正确 err:{0}", err)
        return
    }

    //设置核数
    runtime.GOMAXPROCS(num)

    for i := 0; i < 10; i++ {
        //做个死循环
        go doSomething()
    }

    fmt.Println("程序已经开始执行")

    time.Sleep(time.Second * 30)

    for j := 5; j > 0; j-- {
        fmt.Printf("程序倒计时退出: %ds...\n", j)
        time.Sleep(time.Second)
    }
    fmt.Printf("程序退出")
}

该脚本首先读取用户输入的目标CPU核心数,利用runtime.GOMAXPROCS进行设定,随后启动10个并发协程进入无限循环,并在主线程休眠等待期间观察系统状态。

CPU资源占用实测效果

基于8核处理器的设备环境,按照上述代码分别配置1、2、4、8个核心进行并发调度,实际系统资源监控结果如下所示:

图一 未开始运行

图二 运行1个cpu

图三 运行2个cpu

图四 运行4个cpu

图五 运行8个cpu

Goroutine的调度原理复习

理解底层调度机制对于排查并发瓶颈很有帮助。Go语言采用著名的GMP(Goroutine-Machine-Processor)调度模型,其中G代表用户态协程,P代表调度器上下文及本地运行队列,M代表操作系统内核级的工作线程。当我们在代码中改变GOMAXPROCS时,实质上是在调整可以并行持有P(即运行Goroutine)的M的数量,从而影响系统的总吞吐量。若需深入了解其内部协作细节,建议查阅相关的Go并发工作原理进阶资料。

以上就是关于Go语言中Goroutine的CPU设置方式及相关调度的详细内容,更多关于Golang并发编程的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表