經(jīng)呈埽看到一些帖子,利用goroutine循環(huán)打印循環(huán)變量懂鸵,來check大家對goroutine的理解是否到位偏螺,同時有些帖子個人認為描述的不夠準確并存在異議之處,在這里分享下我的理解匆光。
首先說下goroutine的調(diào)度
1.G代表一個goroutine對象套像,每次go調(diào)用的時候,都會創(chuàng)建一個G對象
2.?M代表一個線程终息,每次創(chuàng)建一個M的時候夺巩,都會有一個底層線程創(chuàng)建;所有的G任務(wù)周崭,最終還是在M上執(zhí)行
3.?P代表一個處理器劲够,每一個運行的M都必須綁定一個P,就像線程必須在么一個CPU核上執(zhí)行一樣
下面說下循環(huán)打印例子:
func main() {
????????for i := 0; i < 100; i++ {
? ? ? ????? go fmt.Println(i)
? ? ????}
}
大家猜想下會輸出什么休傍?
答案1征绎、順序輸出1到100
答案2、亂序輸出1到100
答案3磨取、輸出100個100
答案4人柿、沒有任何輸出。
上面的例子很簡單忙厌。對于選擇1或者2的同學凫岖,可能就進入了出題人的陷阱了??,main函數(shù)很快執(zhí)行結(jié)束程序退出了逢净,甚至根本不給go協(xié)程調(diào)度執(zhí)行的機會哥放。標準答案應(yīng)該是4歼指。那什么情況下會是1、2或者3那甥雕?
首先想讓上面的程序正常有打印踩身,可以在main函數(shù)后面加個sleep,或者sync.WaitGroup機制社露,或者通過channel機制均可挟阻,這里不寫了,如果大家有興趣可以自己寫峭弟,如果寫不出來可單獨留言附鸽。實際上上面的程序1和2都有可能輸出,如上面的goroutine調(diào)度圖可知瞒瘸,取決于P(處理器核數(shù))坷备,如果是在多核上運行,不加任何處理,則輸出結(jié)果如下:
與答案2一致情臭,如果在程序里面增加runtime.GOMAXPROCS(1)省撑,則會順序輸出,與答案1一致谎柄。怎么做能夠輸出3的效果丁侄?答案也很簡單,如下程序即可:
runtime.GOMAXPROCS(1)
for i :=0; i <100; i++ {
????????go func() {
????????????????fmt.Println(i)
????????}()
}
此外看到一篇文章朝巫,里面舉了個例子鸿摇,如下:
func main() {
? ? for i := 0; i < 100; i++ {
? ? ? ? go func(i int) {
? ? ? ? ? ? fmt.Println(i)
? ? ? ? }(i)
? ? }
? ? time.sleep(2 * time.second)
}
簡單的說明這個程序會順序輸出1-100,這么說是不嚴謹?shù)呐常瑢εc單核或者設(shè)定runtime.GOMAXPROCS(1)選項是OK的拙吉,如果沒有指定,還是會亂序輸出的揪荣。
總結(jié)下筷黔,綜上所述,我們在表達一個結(jié)果或者一個一個結(jié)論的時候仗颈,切記把背景描述清楚佛舱。否則會誤人子弟,導(dǎo)致一個面試失敗~~??挨决。