首先我们应该明确,并行和并发的区别,我之前文章中有详细的解释。概要说就是,并发一般都是被内核通过时间片或者中断来控制的,一旦遇到IO阻塞或者时间片用完,就会转移线程的使用权。单核不可能有并行,同一时间只能有一个任务在调度。
Go中runtime.GOMAXPROCS可以设置多核执行任务。并行比较适合cpu计算密集型。如果IO密集型使用多核反而会增加cpu切换的成本。
需要特意说明一点是,你在测试并发的时候,往往会把一个函数写成死循环并做计算,你虽然这段函数用go关键词并发了,但是你会发现他无法执行后面的逻辑。你需要做的是配置多核,或者是time.Sleep()。 这是为什么 ? Golang是自己管理调整goroutine,如果你的一个func始终不释放资源,那么其他的goroutine不会去抢夺资源。 当然这样的场景只有在测试时候遇到,正常场景下不可能没有中断和堵塞的情况。
那为何说设置的核心越多反而会降低性能?看下面代码:
package main
import(
"fmt"
"time"
"runtime"
)
var quit chan int = make(chan int)
func loop(){
for i:=0 ;i<10000; i++{
fmt.Printf("%d\n", i)
}
quit <- 0
}
func main(){
fmt.Println(runtime.NumCPU()) // 默认CPU核心数
time.Sleep(2*time.Second)
a := 5000
t0 := time.Now() // 起点时间
//runtime.GOMAXPROCS(1) // 设置执行使用的核心数
for i:=1; i<= a; i++{
go loop()
}
for i:=0; i< a; i++{
<-quit
}
endTime := time.Since(t0) // 耗时
fmt.Println("运行时间", endTime)
}
首先说明我的电脑是8核ubuntu , 其中runtime.GOMAXPROCS(1) 被注释, 得到的结果是 2m13s 。
取消注释,使用一个核心执行此程序,得到的确实1m 20s。 (都是多次执行取平均值)。
此处还要注意点一点就是: Go默认执行使用的CPU核心数为系统CPU最大核心。
有的码友可能觉得是channel的问题, 多个协程同时写入,带来的并发问题。其实不然,此处瓶颈在于fmt,
由于多个协程同时往stdout写入, 纵然前面执行再快,最后还是要单线程输出到屏幕,导致的性能问题,如果fmt换为其他操作,就可以显示出GOMAXPROCS的好处。