1. 程式人生 > 實用技巧 >【go語言學習】併發實現

【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優先搶到系統資源,就會輸出你好,世界,然後主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