【go語言學習】併發實現
一、什麼是goroutine
Goroutine是Go語言特有的名詞。區別於程序Process,執行緒Thread,協程Coroutine,因為Go語言的創造者們覺得和他們是有所區別的,所以專門創造了Goroutine。
Goroutine是與其他函式或方法同時執行的函式或方法。Goroutines可以被認為是輕量級的執行緒。與執行緒相比,建立Goroutine的成本很小,它就是一段程式碼,一個函式入口。以及在堆上為其分配的一個堆疊(初始大小為4K,會隨著程式的執行自動增長刪除)。因此它非常廉價,Go應用程式可以併發執行數千個Goroutines。
二、主goroutine
封裝main函式的goroutine稱為主goroutine。
主goroutine所做的事情並不是執行main函式那麼簡單。它首先要做的是:設定每一個goroutine所能申請的棧空間的最大尺寸。在32位的計算機系統中此最大尺寸為250MB,而在64位的計算機系統中此尺寸為1GB。如果有某個goroutine的棧空間尺寸大於這個限制,那麼執行時系統就會引發一個棧溢位(stack overflow)的執行時恐慌。隨後,這個go程式的執行也會終止。
此後,主goroutine會進行一系列的初始化工作,涉及的工作內容大致如下:
- 建立一個特殊的defer語句,用於在主goroutine退出時做必要的善後處理。因為主goroutine也可能非正常的結束
- 啟動專用於在後臺清掃記憶體垃圾的goroutine,並設定GC可用的標識
- 執行mian包中的init函式
- 執行main函式
- 執行完main函式後,它還會檢查主goroutine是否引發了執行時恐慌,並進行必要的處理。最後主goroutine會結束自己以及當前程序的執行。
三、如何使用goroutine
Go語言中使用goroutine非常簡單,只需要在呼叫函式的時候在前面加上go關鍵字,就可以為一個函式建立一個goroutine。
一個goroutine必定對應一個函式,可以建立多個goroutine去執行相同的函式。
package main
import (
"fmt"
)
func main() {
go f1()
fmt.Println("你好,世界")
}
func f1() {
fmt.Println("hello world")
}
執行結果
你好,世界
或者
hello world
你好,世界
多次執行以上程式,會出現不同的輸出結果。
這是因為:上面的程式有main函式主goroutine,和f1函式子goroutine。程式執行時,當子goroutine優先搶到系統資源,就會輸出hello world
你好,世界
,結束程式;當主goroutine優先搶到系統資源,就會輸出你好,世界
,然後主goroutine就結束了,所有在main()函式中啟動的goroutine會一同結束。
修改以上程式碼:
package main
import (
"fmt"
"time"
)
func main() {
go f1()
fmt.Println("你好,世界")
time.Sleep(1 * time.Second)
}
func f1() {
fmt.Println("hello world")
}
執行結果
你好,世界
hello world
增加了主goroutine睡眠1s,這樣子goroutine有足夠的時間來執行。
四、啟動多個goroutine
package main
import (
"fmt"
"time"
)
func main() {
go numbers()
go alphabets()
time.Sleep(3000 * time.Millisecond)
fmt.Println(" main over")
}
func numbers() {
for i := 1; i <= 5; i++ {
time.Sleep(250 * time.Millisecond)
fmt.Printf("%d", i)
}
}
func alphabets() {
for i := 'a'; i <= 'e'; i++ {
time.Sleep(400 * time.Millisecond)
fmt.Printf("%c", i)
}
}
執行結果
1a23b4c5de main over