Golang中无goroutine情况下CPU使用率超过100%问题探讨
Golang中无goroutine情况下CPU使用率超过100%问题探讨 大家好,
我是Go语言的新手。我想知道为什么我的Go程序使用了超过100%的CPU。我感到困惑,因为我并没有使用任何goroutine。在下面的测试示例中,在我的机器上执行时,它在短时间内使用了130%和200%的CPU(我还有一个将文件处理成map[string]*Person的程序,它持续以200%的CPU运行)。Go是否会自动并发执行程序?由于我的谷歌搜索能力有限,我找不到任何相关资料。非常感谢您的帮助!
package main
import (
"math/rand"
)
type Person struct {
Name string
ID int
}
func main() {
n := int(10e6)
people := make(map[int]*Person)
for i := 1; i < n; i++ {
person := RandPerson()
people[person.ID] = person
}
println(len(people), "#####", n)
}
func RandPerson() *Person {
name := make([]byte, 50)
for i := 0; i < 50; i++ {
name[i] = byte(65 + rand.Intn(25))
}
id := rand.Intn(10e6)
return &Person{
Name: string(name),
ID: id,
}
}
time ./randperson real 0m11.924s user 0m17.379s sys 0m0.544s
更多关于Golang中无goroutine情况下CPU使用率超过100%问题探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html
垃圾回收。你正在分配大量内存,因此为了释放内存,会有一定量的垃圾回收发生,尽管在这个示例中你并没有产生太多垃圾。
更多关于Golang中无goroutine情况下CPU使用率超过100%问题探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
谢谢。除了垃圾回收,您知道通常还有什么原因会导致程序运行超过1个线程吗?所有这些“无意中”的CPU使用都应该是安全的吗?
在这种情况下避免CPU开销的一个好技巧是在主循环中插入一个小的延迟,例如:
time.Sleep(2*time.Millisecond)
当然,你可以使用一个更小且合适的值……
如果你自己没有进行任何并发处理,或者没有使用进行并发处理的包,那么我预计不会有任何并发处理,不会。但垃圾回收总是与程序的其余部分并发进行。这是正常的、预期的,并且确实是安全的。
在Go语言中,即使没有显式创建goroutine,程序仍然可能显示超过100%的CPU使用率。这通常是由于Go运行时系统的内部并发机制和操作系统报告CPU使用率的方式造成的。
关键原因分析
1. Go运行时的内部并发
Go运行时系统本身会创建多个线程来处理垃圾回收、调度等任务。即使你的代码没有显式使用goroutine,这些后台线程仍然会并发执行。
2. 操作系统统计方式
top或htop等工具显示的CPU百分比是基于单个CPU核心的。100%表示一个核心完全利用,200%表示两个核心完全利用,以此类推。
3. 代码中的性能瓶颈
你的代码存在几个可能导致高CPU使用率的因素:
package main
import (
"math/rand"
"sync"
)
type Person struct {
Name string
ID int
}
func main() {
n := int(10e6)
people := make(map[int]*Person)
// 问题1:rand.Intn()在并发环境下有锁竞争
// 问题2:大量的小内存分配
for i := 1; i < n; i++ {
person := RandPerson()
people[person.ID] = person
}
println(len(people), "#####", n)
}
func RandPerson() *Person {
name := make([]byte, 50)
// 问题3:频繁调用rand.Intn()
for i := 0; i < 50; i++ {
name[i] = byte(65 + rand.Intn(25))
}
id := rand.Intn(10e6)
return &Person{
Name: string(name),
ID: id,
}
}
优化建议和示例
优化版本1:减少rand.Intn()调用
package main
import (
"math/rand"
"time"
)
type Person struct {
Name string
ID int
}
func main() {
rand.Seed(time.Now().UnixNano())
n := int(10e6)
people := make(map[int]*Person, n/10) // 预分配容量
for i := 1; i < n; i++ {
person := RandPersonOptimized()
people[person.ID] = person
}
println(len(people), "#####", n)
}
func RandPersonOptimized() *Person {
// 一次性生成所有随机字节
nameBytes := make([]byte, 50)
rand.Read(nameBytes)
for i := range nameBytes {
nameBytes[i] = (nameBytes[i] % 26) + 65
}
return &Person{
Name: string(nameBytes),
ID: rand.Intn(10e6),
}
}
优化版本2:使用sync.Pool减少内存分配
package main
import (
"math/rand"
"sync"
"time"
)
type Person struct {
Name string
ID int
}
var personPool = sync.Pool{
New: func() interface{} {
return &Person{}
},
}
func main() {
rand.Seed(time.Now().UnixNano())
n := int(10e6)
people := make(map[int]*Person, n/10)
for i := 1; i < n; i++ {
person := RandPersonWithPool()
people[person.ID] = person
personPool.Put(person) // 放回池中
}
println(len(people), "#####", n)
}
func RandPersonWithPool() *Person {
p := personPool.Get().(*Person)
nameBytes := make([]byte, 50)
rand.Read(nameBytes)
for i := range nameBytes {
nameBytes[i] = (nameBytes[i] % 26) + 65
}
p.Name = string(nameBytes)
p.ID = rand.Intn(10e6)
return p
}
监控CPU使用率的示例
package main
import (
"fmt"
"math/rand"
"runtime"
"time"
)
func monitorCPU() {
for {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("Goroutines: %d, CPU Cores: %d\n",
runtime.NumGoroutine(),
runtime.NumCPU())
time.Sleep(2 * time.Second)
}
}
func main() {
// 启动监控
go monitorCPU()
// 你的业务逻辑
n := int(10e6)
people := make(map[int]*Person)
for i := 1; i < n; i++ {
person := RandPerson()
people[person.ID] = person
// 每100万次输出一次进度
if i%1000000 == 0 {
fmt.Printf("Processed: %d/%d\n", i, n)
}
}
println(len(people), "#####", n)
time.Sleep(5 * time.Second) // 给监控goroutine时间输出
}
验证CPU使用率
使用runtime包查看实际并发情况:
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Printf("Number of CPU cores: %d\n", runtime.NumCPU())
fmt.Printf("GOMAXPROCS setting: %d\n", runtime.GOMAXPROCS(0))
fmt.Printf("Number of goroutines: %d\n", runtime.NumGoroutine())
// 即使没有显式创建goroutine,Go运行时也会有一些后台goroutine
// 运行你的程序后,使用以下命令查看:
// 1. top -p $(pidof your_program)
// 2. htop -p $(pidof your_program)
// 3. ps -o pid,pcpu,pmem,comm -p $(pidof your_program)
}
你的程序显示超过100%的CPU使用率是正常的,这表示Go运行时正在有效地利用多个CPU核心。即使没有显式使用goroutine,Go的垃圾回收器、调度器等组件也会并发运行。要降低CPU使用率,需要优化算法和减少不必要的计算,而不是担心这个百分比数字本身。

