Go 并发程序未并行执行的原因及解决方案

Go 并发程序未并行执行的原因及解决方案

本文旨在解答 Go 语言并发程序未能充分利用多核 CPU 资源,导致程序未并行执行的问题。我们将深入探讨 GOMAXPROCS 的作用,分析其对程序性能的影响,并提供相应的优化建议,帮助开发者编写高效的并发 Go 程序。

在 Go 语言中,goroutine 提供了强大的并发能力,但有时我们会发现,即使程序中使用了多个 goroutine,也无法充分利用多核 CPU 的优势,导致程序未能并行执行。这通常与 GOMAXPROCS 的设置有关。

GOMAXPROCS 的作用

GOMAXPROCS 是一个环境变量或 runtime 包中的函数,用于设置 Go 程序可以同时使用的操作系统线程的最大数量。默认情况下,GOMAXPROCS 的值为 1,这意味着 Go 程序只能使用一个操作系统线程,即使 CPU 有多个核心,goroutine 也只能在一个核心上运行,无法实现真正的并行。

要让 Go 程序利用多核 CPU,需要将 GOMAXPROCS 设置为大于 1 的值。通常,可以将其设置为 CPU 的核心数。

如何设置 GOMAXPROCS

有两种方法可以设置 GOMAXPROCS:

  1. 设置环境变量:

    在运行 Go 程序之前,可以在终端中设置 GOMAXPROCS 环境变量。例如,如果 CPU 有 4 个核心,可以执行以下命令:

    export GOMAXPROCS=4 go run your_program.go
  2. 使用 runtime.GOMAXPROCS 函数:

    可以在 Go 代码中使用 runtime.GOMAXPROCS 函数来设置 GOMAXPROCS 的值。例如:

    package main  import (     "fmt"     "runtime" )  func main() {     runtime.GOMAXPROCS(runtime.NumCPU()) // 设置 GOMAXPROCS 为 CPU 核心数     fmt.Println("GOMAXPROCS:", runtime.GOMAXPROCS(0)) // 打印 GOMAXPROCS 的值     // ... 你的并发代码 ... }

    runtime.NumCPU() 函数返回 CPU 的逻辑核心数。

示例代码分析与改进

让我们回顾一下原始代码,并添加设置 GOMAXPROCS 的代码:

package main  import (     "fmt"     "time"     "big"     "runtime" )  var c chan *big.Int  func sum(start, stop, step int64) {     bigstop := big.NewInt(stop)     bigStep := big.NewInt(step)     bigSum := big.NewInt(0)     for i := big.NewInt(start); i.Cmp(bigStop) < 0; i.Add(i, bigStep) {         bigSum.Add(bigSum, i)     }     c <- bigSum }  func main() {     runtime.GOMAXPROCS(runtime.NumCPU()) // 设置 GOMAXPROCS 为 CPU 核心数     fmt.Println("GOMAXPROCS:", runtime.GOMAXPROCS(0)) // 打印 GOMAXPROCS 的值      s := big.NewInt(0)     n := time.Nanoseconds()      step := int64(4)     c := make(chan *big.Int, int(step))     stop := int64(100000000)     for j := int64(0); j < step; j++ {         go sum(j, stop, step)     }     for j := int64(0); j < step; j++ {         s.Add(s, <-c)     }     n = time.Nanoseconds() - n     fmt.Println(s, float64(n)/1000000000.) }

通过在 main 函数中调用 runtime.GOMAXPROCS(runtime.NumCPU()),我们可以确保 Go 程序能够利用所有可用的 CPU 核心。

注意事项

  • 并发不等于并行: 即使设置了 GOMAXPROCS,也不能保证程序一定能够并行执行。并发是指程序能够同时处理多个任务,而并行是指程序能够同时在多个 CPU 核心上执行多个任务。Go 的 goroutine 提供了并发能力,但只有当 GOMAXPROCS 大于 1 时,才能实现真正的并行。
  • Context switching 的开销: 如果程序中存在大量的 goroutine 之间的通信,设置过大的 GOMAXPROCS 可能会导致频繁的 context switching,反而降低程序的性能。因此,需要根据程序的具体情况,选择合适的 GOMAXPROCS 值。
  • Goroutine 调度器的优化: Go 的 goroutine 调度器也在不断优化,未来可能会更好地处理 goroutine 之间的通信,减少 context switching 的开销。

总结

GOMAXPROCS 是控制 Go 程序并行执行的关键参数。通过正确设置 GOMAXPROCS,可以充分利用多核 CPU 的优势,提高程序的性能。然而,需要注意并发不等于并行,以及 context switching 的开销,根据程序的具体情况选择合适的 GOMAXPROCS 值。在编写并发 Go 程序时,理解 GOMAXPROCS 的作用至关重要。

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享