Go協(xié)程
1.什么是goroutine?
Go在語(yǔ)言級(jí)別原生支持并發(fā)操作,這在現(xiàn)代眾多基于線程并發(fā)的其他語(yǔ)言來(lái)看是比較鶴立雞群的。在Go中最基本的并發(fā)任務(wù)單元是一種稱為goroutine的東西,我們把它叫做協(xié)程或go程,其開(kāi)一個(gè)并發(fā)任務(wù)簡(jiǎn)單到令人發(fā)指,只需go關(guān)鍵字,就能讓一個(gè)函數(shù)成為并發(fā)任務(wù)。
示例:
//啟動(dòng)go程并發(fā)
func BaseGoroutine01() {
//開(kāi)辟一條協(xié)程
go func() {
fmt.Println("這里是獨(dú)立開(kāi)辟的協(xié)程打印的?。?!")
}()
//開(kāi)辟十條協(xié)程
for i := 1; i <= 10; i++ {
go func(num int) {
fmt.Println("這里由協(xié)程", num, "打?。。?!")
}(i)
}
//把一個(gè)命名函數(shù)作為協(xié)程運(yùn)行
go goroutineFunc()
fmt.Println("這里由父協(xié)程打?。。?!")
//父協(xié)程必須留有足夠長(zhǎng)時(shí)間讓子協(xié)程運(yùn)行完,暫時(shí)用睡眠
time.Sleep(time.Second)
}
func goroutineFunc() {
for i := 1; i <= 10; i++ {
fmt.Println(i)
}
}
go的協(xié)程有如下幾個(gè)特點(diǎn):
- Go的協(xié)程是一個(gè)輕量級(jí)的線程,多個(gè)協(xié)程可能運(yùn)行在一個(gè)或者多個(gè)線程上;
- Go的協(xié)程是非搶占式多任務(wù)處理,需要由協(xié)程主動(dòng)交出控制權(quán),這與其他搶占型的線程并發(fā)方式不同,這種非搶占式設(shè)計(jì)減少了許多cpu調(diào)度切換的開(kāi)銷(xiāo);
- Go的協(xié)程是一個(gè)虛擬機(jī)層面的多任務(wù)處理,它基于Go的并發(fā)調(diào)度器,其調(diào)度是go運(yùn)行時(shí)自動(dòng)管理的,對(duì)用戶不可見(jiàn),用戶只需管理業(yè)務(wù)層面的并發(fā)即可。
- Go中所有的調(diào)用棧都是一個(gè)協(xié)程,main()函數(shù)就是主協(xié)程,如無(wú)開(kāi)辟其他協(xié)程所有任務(wù)都在主協(xié)程運(yùn)行。
然而,go的協(xié)程雖然號(hào)稱能開(kāi)百萬(wàn)級(jí),但也不是無(wú)節(jié)制的是亂開(kāi)線程,其也是有一些規(guī)則甚至是陷阱的,下面我們來(lái)了解一些go的協(xié)程特性及用法:
2.go協(xié)程特性:主死從隨
主協(xié)程沒(méi)有阻塞的執(zhí)行完,程序結(jié)束,子協(xié)程無(wú)論有無(wú)執(zhí)行完都會(huì)結(jié)束。
func main() {
go func() {
for i := 1; i <= 10; i++ {
fmt.Println("子協(xié)程輸出", i)
time.Sleep(time.Second)
}
}()
//主協(xié)程只存活5s
for i := 1; i <= 5; i++ {
fmt.Println("主協(xié)程輸出", i)
time.Sleep(time.Second)
}
}
3. go協(xié)程特性:出讓協(xié)程資源
go的協(xié)程是非搶占式的,占有cpu資源的協(xié)程如無(wú)主動(dòng)讓出,其他協(xié)程只能等待:使用runtime.Gosched()進(jìn)行控制
func BaseGoroutine03() {
for i := 1; i <= 10; i++ {
go func(n int) {
//出讓奇數(shù)協(xié)程資源
if n%2 == 1 {
//這里使用runtime包控制協(xié)程的cpu資源讓出
runtime.Gosched()
}
for j := 1; j <= 5; j++ {
fmt.Println("第", n, "協(xié)程:第", j, "次打印")
}
}(i)
}
time.Sleep(time.Second * 3)
}
4.go協(xié)程特性:設(shè)置并查看可用cpu核心數(shù)
在如今的多核時(shí)代,go程序能夠充分利用cpu核心數(shù),其runtime包中提供可設(shè)置查看可用cpu核心數(shù)的函數(shù):runtime.GOMAXPROCS()
func BaseGoroutine04() {
numCPU := runtime.NumCPU()
fmt.Println("CPU邏輯核心數(shù):", numCPU)
//GOMAXPROCS設(shè)置可同時(shí)執(zhí)行的最大CPU數(shù),并返回先前的設(shè)置
prevNum := runtime.GOMAXPROCS(2)
fmt.Println("設(shè)置CPU核心數(shù)為2,設(shè)置前為", prevNum)
}
5.go協(xié)程特性:協(xié)程退出
使用runtime.Goexit()可讓協(xié)程退出,父協(xié)程和子協(xié)程各自退出的后果不同,子協(xié)程退出后提前執(zhí)行defer,非善終(協(xié)程不會(huì)正常返回),父協(xié)程退出會(huì)令子協(xié)程失去牽制,主從皆死程序崩潰。
//子協(xié)程退出
func BaseGoroutine05() {
go func() {
for i := 1; i <= 5; i++ {
if i == 3 {
fmt.Println("子協(xié)程打印到3,協(xié)程退出!")
runtime.Goexit()
}
fmt.Println("子協(xié)程輸出", i)
time.Sleep(time.Second)
}
}()
for i := 1; i <= 10; i++ {
fmt.Println("主協(xié)程打印", i)
time.Sleep(time.Second)
}
}
6.go協(xié)程特性:協(xié)程間按調(diào)度器隊(duì)列執(zhí)行,公平使用資源
非搶占式的并發(fā)模型不會(huì)主動(dòng)占有cpu資源,開(kāi)啟協(xié)程后其會(huì)在調(diào)度器中的協(xié)程隊(duì)列排隊(duì),除非別人出讓資源,否則按隊(duì)列執(zhí)行,關(guān)于Go的并發(fā)調(diào)度模型會(huì)另開(kāi)專題討論。
//協(xié)程簡(jiǎn)公平使用資源
func BaseGoroutine06() {
go func() {
for i := 1; i <= 10; i++ {
fmt.Println("協(xié)程1:自己排隊(duì)!")
time.Sleep(time.Nanosecond)
}
}()
go func() {
for i := 1; i <= 10; i++ {
fmt.Println("協(xié)程2:自己排隊(duì)!")
time.Sleep(time.Nanosecond)
}
}()
time.Sleep(time.Second)
}