這是『就要學習 Go 語言』系列的第 21 篇分享文章
併發與並行
提到併發,相信大家還聽過另一個概念 -- 並行。我先給大家介紹下這兩者之間的區別,再來講 Go 語言的併發。
並行其實很好理解,就是同時執行的意思,在某一時間點能夠執行多個任務。 想達到並行效果,最簡單的方式就是藉助多執行緒或多程式,這樣才可在同一時刻執行多個任務。單執行緒是永遠無法達到並行狀態的。 併發是在某一時間段內可以同時處理多個任務。我們通常會說程式是併發設計的,也就是說它允許多個任務同時執行,這個同時指的就是一段時間內。單執行緒中多個任務以間隔執行實現併發。 可以說,多執行緒或多程式是並行的基礎,但單執行緒也通過協程實現了併發。
舉個常見的例子,一臺單核電腦可以下載、聽音樂,實際上這兩個任務是這樣執行的,只不過這兩個任務切換時間短,給人的感覺是同時執行的。
一臺多核電腦的任務執行就是像下面這種圖顯示的一樣: 可以看到,同一時刻能執行多個任務。這種任務執行方式才是真正的並行。Go 通過協程實現併發,協程之間靠通道通訊,本篇文章先給大家介紹協程的使用,後面再寫通道。
協程
協程(Goroutine)可以理解成輕量級的執行緒,但與執行緒相比,它的開銷非常小。因此,Go 應用程式通常能併發地執行成千上萬的協程。 Go 建立一個協程非常簡單,只要在方法或函式呼叫之前加關鍵字 go 即可。
func printHello() {
fmt.Println("hello world goroutine")
}
func main() {
go printHello() // 建立了協程
fmt.Println("main goroutine")
}
複製程式碼
輸出:
main goroutine
複製程式碼
上面程式碼,第 6 行使用 go 關鍵字建立了協程,現在有兩個協程,新建立的協程和主協程。printHello() 函式將會獨立於主協程併發地執行。 是的,你沒有看錯,程式的輸出就是這樣,不信?你可以實際執行下程式。你驚訝的是,printHello() 函式為什麼沒有輸出,到底發生了什麼? 當協程建立完畢之後,主函式立即返回繼續執行下一行程式碼,不像函式呼叫,需要等函式執行完成。主協程執行完畢,程式便退出,printHello 協程隨即也退出,便不會有輸出。
修改下程式碼:
func printHello() {
fmt.Println("hello world goroutine")
}
func main() {
go printHello()
time.Sleep(1*time.Second)
fmt.Println("main goroutine")
}
複製程式碼
協程建立完成之後,main 協程先休眠 1s,預留給 printHello 協程執行的時間,所以這次輸出:
hello world goroutine
main goroutine
複製程式碼
建立多個協程
上一節就提到,可以建立多個協程,來看下例子:
func printNum() {
for i := 1; i <= 5; i++ {
time.Sleep(20 * time.Millisecond)
fmt.Printf("%d ", i)
}
}
func printChacter() {
for i := 'a'; i <= 'e'; i++ {
time.Sleep(40 * time.Millisecond)
fmt.Printf("%c ", i)
}
}
func main() {
go printNum()
go printChacter()
time.Sleep(3*time.Second)
fmt.Println("main terminated")
}
複製程式碼
上面的程式碼,除主協程之外,新建立了兩個協程:printNum 協程和 printChacter 協程。printNum 協程每隔 20 毫秒輸出 5 個數,printChacter 協程每隔 40 毫秒輸出字母。主協程建立完這兩個協程之後休眠 1s,等待其他協程執行完成。 程式輸出(你的輸出可能跟我的不一樣):
1 a 2 3 b 4 5 c d e main terminated
複製程式碼
話說回來,通過加 time.Sleep() 函式等待協程執行完成是一種“黑科技”。在實際生產環境中,不管我們是否知道其他協程執行完成需要多少時間,都不能在主協程中新增隨機睡眠呼叫等待其他協程執行完成。那怎麼辦?Go 給我們提供了通道,當協程執行完畢,能夠通知到主協程,還能夠實現協程間通訊。下節課我們來討論一下。
匿名協程
在函式那篇文章講過,存在匿名函式,通過關鍵字 go 呼叫匿名函式就是匿名協程。我們修改之前的例子:
func main() {
go func() {
fmt.Println("hello world goroutine")
}()
time.Sleep(1*time.Second)
fmt.Println("main goroutine")
}
複製程式碼
輸出結果跟之前的一樣。
希望這篇文章給你帶來收穫,Good Day !
(全文完)
原創文章,若需轉載請註明出處!
歡迎掃碼關注公眾號「Golang來啦」或者移步 seekload.net ,檢視更多精彩文章。
給你準備了學習 Go 語言相關書籍,公號後臺回覆【電子書】領取!