Go 併發 -- 協程

這是『就要學習 Go 語言』系列的第 21 篇分享文章markdown

併發與並行

提到併發,相信你們還聽過另外一個概念 -- 並行。我先給你們介紹下這二者之間的區別,再來說 Go 語言的併發。多線程

並行其實很好理解,就是同時執行的意思,在某一時間點可以執行多個任務。 想達到並行效果,最簡單的方式就是藉助多線程或多進程,這樣纔可在同一時刻執行多個任務。單線程是永遠沒法達到並行狀態的。 併發是在某一時間段內能夠同時處理多個任務。咱們一般會說程序是併發設計的,也就是說它容許多個任務同時執行,這個同時指的就是一段時間內。單線程中多個任務以間隔執行實現併發。 能夠說,多線程或多進程是並行的基礎,但單線程也經過協程實現了併發。併發

舉個常見的例子,一臺單核電腦能夠下載、聽音樂,實際上這兩個任務是這樣執行的,只不過這兩個任務切換時間短,給人的感受是同時執行的。 函數

併發
一臺多核電腦的任務執行就是像下面這種圖顯示的同樣:
並行
能夠看到,同一時刻能執行多個任務。這種任務執行方式纔是真正的並行。

Go 經過協程實現併發,協程之間靠信道通訊,本篇文章先給你們介紹協程的使用,後面再寫信道。oop

協程

協程(Goroutine)能夠理解成輕量級的線程,但與線程相比,它的開銷很是小。所以,Go 應用程序一般能併發地運行成千上萬的協程。 Go 建立一個協程很是簡單,只要在方法或函數調用以前加關鍵字 go 便可。學習

func printHello() {
	fmt.Println("hello world goroutine")
}

func main() {
	go printHello()    // 建立了協程
	fmt.Println("main goroutine")
}
複製代碼

輸出:spa

main goroutine
複製代碼

上面代碼,第 6 行使用 go 關鍵字建立了協程,如今有兩個協程,新建立的協程和主協程。printHello() 函數將會獨立於主協程併發地執行。 是的,你沒有看錯,程序的輸出就是這樣,不信?你能夠實際運行下程序。你驚訝的是,printHello() 函數爲何沒有輸出,到底發生了什麼? 當協程建立完畢以後,主函數當即返回繼續執行下一行代碼,不像函數調用,須要等函數執行完成。主協程執行完畢,程序便退出,printHello 協程隨即也退出,便不會有輸出。.net

修改下代碼:線程

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 語言相關書籍,公號後臺回覆【電子書】領取!

公衆號二維碼
相關文章
相關標籤/搜索