Goroutine詳解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ù)就可以了,就是這么簡(jiǎn)單粗暴 。
使用goroutineGo語言中使用goroutine非常簡(jiǎn)單,只需要在調(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ù) 。
啟動(dòng)單個(gè)goroutine啟動(dòng)goroutine的方式非常簡(jiǎn)單,只需要在調(diào)用的函數(shù)(普通函數(shù)和匿名函數(shù))前面加上一個(gè)go關(guān)鍵字 。
舉個(gè)例子如下:
func hello() {fmt.Println("Hello Goroutine!")}func main() {hello()fmt.Println("main goroutine done!")}【Go_Goroutine詳解】這個(gè)示例中hello函數(shù)和下面的語句是串行的,執(zhí)行的結(jié)果是打印完Hello Goroutine!后打印main goroutine done! 。
接下來我們?cè)谡{(diào)用hello函數(shù)前面加上關(guān)鍵字go,也就是啟動(dòng)一個(gè)goroutine去執(zhí)行hello這個(gè)函數(shù) 。
func main() {go hello() // 啟動(dòng)另外一個(gè)goroutine去執(zhí)行hello函數(shù)fmt.Println("main goroutine done!")}這一次的執(zhí)行結(jié)果只打印了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ù),最簡(jiǎn)單粗暴的方式就是time.Sleep了 。
func main() {go hello() // 啟動(dòng)另外一個(gè)goroutine去執(zhí)行hello函數(shù)fmt.Println("main goroutine done!")time.Sleep(time.Second)}執(zhí)行上面的代碼你會(huì)發(fā)現(xiàn),這一次先打印main goroutine done!,然后緊接著打印Hello Goroutine! 。
首先為什么會(huì)先打印main goroutine done!是因?yàn)槲覀冊(cè)趧?chuàng)建新的goroutine的時(shí)候需要花費(fèi)一些時(shí)間,而此時(shí)main函數(shù)所在的goroutine是繼續(xù)執(zhí)行的 。
啟動(dòng)多個(gè)goroutine在Go語言中實(shí)現(xiàn)并發(fā)就是這樣簡(jiǎn)單,我們還可以啟動(dòng)多個(gè)goroutine 。讓我們?cè)賮硪粋€(gè)例子: (這里使用了sync.WaitGroup來實(shí)現(xiàn)goroutine的同步)
var wg sync.WaitGroupfunc hello(i int) {defer wg.Done() // goroutine結(jié)束就登記-1fmt.Println("Hello Goroutine!", i)}func main() {for i := 0; i < 10; i++ {wg.Add(1) // 啟動(dòng)一個(gè)goroutine就登記+1go hello(i)}wg.Wait() // 等待所有登記的goroutine都結(jié)束}多次執(zhí)行上面的代碼,會(huì)發(fā)現(xiàn)每次打印的數(shù)字的順序都不一致 。這是因?yàn)?0個(gè)goroutine是并發(fā)執(zhí)行的,而goroutine的調(diào)度是隨機(jī)的 。
goroutine與線程可增長(zhǎng)的棧OS線程(操作系統(tǒng)線程)一般都有固定的棧內(nèi)存(通常為2MB),一個(gè)goroutine的棧在其生命周期開始時(shí)只有很小的棧(典型情況下2KB),goroutine的棧不是固定的,他可以按需增大和縮小,goroutine的棧大小限制可以達(dá)到1GB,雖然極少會(huì)用到這個(gè)大 。所以在Go語言中一次創(chuàng)建十萬左右的goroutine也是可以的 。
goroutine調(diào)度GPM是Go語言運(yùn)行時(shí)(runtime)層面的實(shí)現(xiàn),是go語言自己實(shí)現(xiàn)的一套調(diào)度系統(tǒng) 。區(qū)別于操作系統(tǒng)調(diào)度OS線程 。
- 1.G很好理解,就是個(gè)goroutine的,里面除了存放本goroutine信息外 還有與所在P的綁定等信息 。
經(jīng)驗(yàn)總結(jié)擴(kuò)展閱讀
- Go_Channel詳解
- Docker | 容器數(shù)據(jù)卷詳解
- MyBatis之ResultMap的association和collection標(biāo)簽詳解
- Future詳解
- Go的網(wǎng)絡(luò)編程詳解
- gorm中的關(guān)聯(lián)操作詳解
- Go中的閉包、遞歸
- liunx之expect操作詳解
- 條件期望:Conditional Expectation 舉例詳解之入門之入門之草履蟲都說聽懂了
- 深入理解AQS--jdk層面管程實(shí)現(xiàn)【管程詳解的補(bǔ)充】
