go 中runtime 類似 Java 和 .NET 語(yǔ)言所用到的虛擬機(jī),它負(fù)責(zé)管理包括內(nèi)存分配、垃圾回收、棧處理、goroutine、channel、切片(slice)、map 和反射(reflection)等等。
runtime 調(diào)度器是個(gè)非常有用的東西,關(guān)于 runtime 包幾個(gè)方法:
.Gosched:讓當(dāng)前線程讓出 cpu 以讓其它線程運(yùn)行,它不會(huì)掛起當(dāng)前線程,因此當(dāng)前線程未來(lái)會(huì)繼續(xù)執(zhí)行
.NumCPU:返回當(dāng)前系統(tǒng)的 CPU 核數(shù)量
.GOMAXPROCS:設(shè)置最大的可同時(shí)使用的 CPU 核數(shù)
.Goexit:退出當(dāng)前 goroutine(但是defer語(yǔ)句會(huì)照常執(zhí)行)
.NumGoroutine:返回正在執(zhí)行和排隊(duì)的任務(wù)總數(shù)
.GOOS:目標(biāo)操作系統(tǒng)
1、Gosched
暫停當(dāng)前goroutine,使其他goroutine先行運(yùn)算。只是暫停,不是掛起,當(dāng)時(shí)間片輪轉(zhuǎn)到該協(xié)程時(shí),Gosched()后面的操作將自動(dòng)恢復(fù)
#未使用Gosched的代碼
package main
import (
"fmt"
)
func main() {
go output("goroutine 2")
output("goroutine 1")
}
func output(s string){
for i:=0;i<3;i++{
fmt.Println(s)
}
}
#輸出
#goroutine 1
#goroutine 1
#goroutine 1
#**結(jié)論:**還沒(méi)等到子協(xié)程執(zhí)行,主協(xié)程就已經(jīng)執(zhí)行完退出了,子協(xié)程將不再執(zhí)行,所以打印的全部是主協(xié)程的數(shù)據(jù)。當(dāng)然,實(shí)際上這個(gè)執(zhí)行結(jié)果也是不確定的,只是大概率出現(xiàn)以上輸出,因?yàn)橹鲄f(xié)程和子協(xié)程間并沒(méi)有絕對(duì)的順序關(guān)系
#使用Gosched的代碼
package main
import (
"fmt"
"runtime"
)
func main() {
go output("goroutine 2")
runtime.Gosched()
output("goroutine 1")
}
func output(s string){
for i:=0;i<3;i++{
fmt.Println(s)
}
}
#輸出:
#goroutine 2
#goroutine 2
#goroutine 2
#goroutine 1
#goroutine 1
#goroutine 1
#**結(jié)論:**在打印goroutine 1之前,主協(xié)程調(diào)用了runtime.Gosched()方法,暫停了主協(xié)程。子協(xié)程獲得了調(diào)度,從而先行打印了goroutine 2。主協(xié)程不是一定要等其他協(xié)程執(zhí)行完才會(huì)繼續(xù)執(zhí)行,而是一定時(shí)間。如果這個(gè)時(shí)間內(nèi)其他協(xié)程沒(méi)有執(zhí)行完,那么主協(xié)程將繼續(xù)執(zhí)行,例如以下例子
#使用Gosched的代碼,并故意延長(zhǎng)子協(xié)程的執(zhí)行時(shí)間,看主協(xié)程是否一直等待
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
go func() {
time.Sleep(5000)
output("goroutine 2")
}()
runtime.Gosched()
output("goroutine 1")
}
func output(s string) {
for i := 0; i < 3; i++ {
fmt.Println(s)
}
}
#輸出:
#goroutine 1
#goroutine 1
#goroutine 1
2、Goexit
立即終止當(dāng)前協(xié)程,不會(huì)影響其它協(xié)程,且終止前會(huì)調(diào)用此協(xié)程聲明的defer方法。由于Goexit不是panic,所以recover捕獲的error會(huì)為nil
當(dāng)main方法所在主協(xié)程調(diào)用Goexit時(shí),Goexit不會(huì)return,所以主協(xié)程將繼續(xù)等待子協(xié)程執(zhí)行,當(dāng)所有子協(xié)程執(zhí)行完時(shí),程序報(bào)錯(cuò)deadlock
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
go func(){
defer func(){
fmt.Println("defer func executed!")
fmt.Println("recovered error == ",recover())
}()
for i:=0;i<3;i++{
if i==1{
runtime.Goexit()
}
fmt.Println(i)
}
}()
time.Sleep(2*time.Second)
}
#輸出:
#0
#defer func executed!
#recovered error == <nil>
3、GOMAXPROCS(n int) int
設(shè)置可同時(shí)執(zhí)行的邏輯Cpu數(shù)量,默認(rèn)和硬件的線程數(shù)一致而不是核心數(shù),可以通過(guò)調(diào)用GOMAXPROCS(-1)來(lái)獲取當(dāng)前邏輯Cpu數(shù)
最好在main函數(shù)之前設(shè)置它,GOMAXPROCS同時(shí)也是go的環(huán)境變量之一
當(dāng)n<1:非法數(shù)字,方法不作修改
當(dāng)n==1:?jiǎn)魏诵?,多協(xié)程并發(fā)執(zhí)行,并發(fā)只是看起來(lái)是同時(shí)執(zhí)行的,實(shí)際上是同一時(shí)刻只有一個(gè)協(xié)程在跑,只是由于cpu的任務(wù)調(diào)度算法,讓多個(gè)協(xié)程在效果上同時(shí)執(zhí)行
當(dāng)n>1:多核心,多協(xié)程并行執(zhí)行,并行一定是并發(fā),不同的核心同時(shí)地跑不同的協(xié)程