Go語言中g(shù)oroutine的使用
一、什么是 Goroutine ?
在java/c++
中我們要實(shí)現(xiàn)并發(fā)編程的時(shí)候,我們通常需要自己維護(hù)一個(gè)線程池,并且需要自己去包裝一個(gè)又一個(gè)的任務(wù),同時(shí)需要自己去調(diào)度線程執(zhí)行任務(wù)并維護(hù)上下文切換,這一切通常會(huì)耗費(fèi)程序員大量的心智。那么能不能有一種機(jī)制,程序員只需要定義很多個(gè)任務(wù),讓系統(tǒng)去幫助我們把這些任務(wù)分配到CPU
上實(shí)現(xiàn)并發(fā)執(zhí)行呢?
Go
語言中的goroutine
就是這樣一種機(jī)制,goroutine
的概念類似于線程,但 goroutine
是由Go的運(yùn)行時(shí)(runtime
)調(diào)度和管理的。Go
程序會(huì)智能地將 goroutine
中的任務(wù)合理地分配給每個(gè)CPU
。Go
語言之所以被稱為現(xiàn)代化的編程語言,就是因?yàn)樗谡Z言層面已經(jīng)內(nèi)置了調(diào)度和上下文切換的機(jī)制。
在Go
語言編程中你不需要去自己寫進(jìn)程、線程、協(xié)程,你的技能包里只有一個(gè)技能–goroutine
,當(dāng)你需要讓某個(gè)任務(wù)并發(fā)執(zhí)行的時(shí)候,你只需要把這個(gè)任務(wù)包裝成一個(gè)函數(shù),開啟一個(gè)goroutine
去執(zhí)行這個(gè)函數(shù)就可以了,就是這么簡單粗暴。
二、使用 Goroutine
Go
語言中使用goroutine
非常簡單,只需要在調(diào)用函數(shù)的時(shí)候在前面加上go
關(guān)鍵字,就可以為一個(gè)函數(shù)創(chuàng)建一個(gè)goroutine
。
一個(gè)goroutine
必定對(duì)應(yīng)一個(gè)函數(shù),可以創(chuàng)建多個(gè)goroutine
去執(zhí)行相同的函數(shù)。
單個(gè) goroutine
package main import "fmt" func hello() { fmt.Println("Hello Goroutine!") } func main() { go hello() fmt.Println("main goroutine done!") }
執(zhí)行這段程序:
main goroutine done!
那么問題來了:為什么 Hello Goroutine!
沒有輸出呢?
在程序啟動(dòng)時(shí),Go
程序就會(huì)為main()
函數(shù)創(chuàng)建一個(gè)默認(rèn)的goroutine
。
當(dāng)main()
函數(shù)返回的時(shí)候該goroutine
就結(jié)束了,所以在main()
函數(shù)中啟動(dòng)的goroutine
會(huì)一同結(jié)束,main
函數(shù)所在的goroutine
就像是權(quán)利的游戲中的夜王,其他的goroutine
都是異鬼,夜王一死它轉(zhuǎn)化的那些異鬼也就全部GG
了。
所以咱們要想辦法讓main
函數(shù)等一等hello
函數(shù),最簡單粗暴的方式就是time.Sleep
了。
示例:
package main import "fmt" func hello() { fmt.Println("Hello Goroutine!") } func main() { go hello() fmt.Println("main goroutine done!") time.Sleep(time.Second) // 等待1秒 }
執(zhí)行這段程序:
Hello Goroutine!
main goroutine done!
多個(gè) goroutine
package main import "fmt" func hello() { fmt.Println("Hello Goroutine!") } func word() { fmt.Println("Word Goroutine!") } func main() { go hello() go word() fmt.Println("main goroutine done!") time.Sleep(2 * time.Second) // 等待2秒 }
執(zhí)行這段程序:
Hello Goroutine!
Word Goroutine!
main goroutine done!
開啟多個(gè) goroutine
只需要再使用一次go
關(guān)鍵字就可以了。
同樣的,咱們?yōu)榱俗屗軌蛲暾敵?,使?time.Sleep
讓main
函數(shù)等待 goroutine
執(zhí)行完成。
sync.WaitGroup 的使用
上面咱們講了如何開啟goroutine
,為了goroutine
正常輸出,增加了 time.Sleep
等待。
但在我們實(shí)際項(xiàng)目開發(fā)中,生硬的使用time.Sleep
肯定是不合適的。那我們應(yīng)該如何才能正確優(yōu)雅的讓 main
函數(shù)等待 goroutine
執(zhí)行完之后再執(zhí)行呢?
go
語言提供了一個(gè) sync.WaitGroup
的一個(gè)計(jì)數(shù)器的功能??梢杂脕韮?yōu)雅的實(shí)現(xiàn) goroutine
的正常執(zhí)行和 main
函數(shù)的等待。
示例:
package main import ( "fmt" "sync" ) var wg sync.WaitGroup func hello(){ defer wg.done() // 計(jì)數(shù)器 - 1 fmt.Println("Hello Goroutine!") } func main(){ wg.Add(1) // 計(jì)數(shù)器 + 1 go hello() wg.wait() // 阻塞直到計(jì)數(shù)器變?yōu)? fmt.Println("main goroutine done!") }
執(zhí)行這段程序:
Hello Goroutine!
main goroutine done!
方法名 | 功能 |
---|---|
(wg * WaitGroup) Add(delta int) | 計(jì)數(shù)器+delta |
(wg *WaitGroup) Done() | 計(jì)數(shù)器-1 |
(wg *WaitGroup) Wait() | 阻塞直到計(jì)數(shù)器變?yōu)? |
sync.WaitGroup
內(nèi)部維護(hù)著一個(gè)計(jì)數(shù)器,計(jì)數(shù)器的值可以增加和減少。例如當(dāng)我們啟動(dòng)了 N
個(gè)并發(fā)任務(wù)時(shí),就將計(jì)數(shù)器值增加N
。每個(gè)任務(wù)完成時(shí)通過調(diào)用Done()
方法將計(jì)數(shù)器減1。通過調(diào)用Wait()
來等待并發(fā)任務(wù)執(zhí)行完,當(dāng)計(jì)數(shù)器值為0時(shí),表示所有并發(fā)任務(wù)已經(jīng)完成。
在實(shí)際項(xiàng)目中使用sync.WaitGroup
可以更好的、更優(yōu)雅的控制goroutine
。
三. 結(jié)束語
到此這篇關(guān)于Go語言中g(shù)oroutine的使用的文章就介紹到這了,更多相關(guān)Go語言goroutine內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go實(shí)現(xiàn)thrift的網(wǎng)絡(luò)傳輸性能及需要注意問題示例解析
這篇文章主要為大家介紹了go實(shí)現(xiàn)thrift的網(wǎng)絡(luò)傳輸性能及需要注意問題示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09Go語言基礎(chǔ)學(xué)習(xí)之Context的使用詳解
在Go語言中,Context是一個(gè)非常重要的概念,它用于在不同的?goroutine?之間傳遞請(qǐng)求域的相關(guān)數(shù)據(jù),本文將深入探討Go語言中?Context特性和Context的高級(jí)使用方法,希望對(duì)大家有所幫助2023-05-05