1. 程式人生 > 實用技巧 >Go語言學習之Go協程(goroutine)

Go語言學習之Go協程(goroutine)

說到Go語言,很多沒接觸過它的人,對它的第一印象,一定是它從語言層面天生支援併發,非常方便,讓開發者能快速寫出高效能且易於理解的程式。

在 Python (為Py為例,主要是我比較熟悉,其他主流程式語言也類似)中,併發程式設計的門檻並不低,你要學習多程序,多執行緒,還要掌握各種支援併發的庫 asyncio,aiohttp 等,同時你還要清楚它們之間的區別及優缺點,懂得在不同的場景選擇不同的併發模式。

而 Golang 作為一門現代化的程式語言,它不需要你直面這些複雜的問題。在 Golang 裡,你不需要學習如何建立程序池/執行緒池,也不需要知道什麼情況下使用多執行緒,什麼時候使用多程序。因為你沒得選,也不需要選,它原生提供的 goroutine (也即協程)已經足夠優秀,能夠自動幫你處理好所有的事情,而你要做的只是執行它,就這麼簡單。

一個 goroutine 本身就是一個函式,當你直接呼叫時,它就是一個普通函式,如果你在呼叫前加一個關鍵字go,那你就開啟了一個 goroutine

// 執行一個函式
func()

// 開啟一個協程執行這個函式
go func()

1.協程的初步使用

一個Go程式的入口通常是main,程式啟動後,main函式最先執行,我們稱之為main goroutine

在main中或者其下呼叫的程式碼中才可以使用go + func()的方法來啟動協程

main的地位相當於主執行緒,當main函式執行完成後,這個執行緒也就終結了,其下執行著的所有協程也不管程式碼是不是還在跑,也得乖乖退出

因此如下程式碼執行完,只會輸出hello,world

,而不會輸出hello,go(因為協程的建立需要時間,當hello,world列印後,協程還沒來得及建立並執行)

import "fmt"

func mytest() {
    fmt.Println("hello, go")
}

func main() {
    // 啟動一個協程
    go mytest()
    fmt.Println("hello, world")
}

對於剛學習Go的協程的人來說,可以使用time.Sleep來是main阻塞,使其他協程能夠有機會執行完全,但你要注意的是 這並不是推薦的方式

當我在程式碼里加入一行time.Sleep輸出就符合預期了

import (
    
"fmt" "time" ) func mytest() { fmt.Println("hello, go") } func main() { go mytest() fmt.Println("hello, world") time.Sleep(time.Second) }

輸出如下

hello, world
hello, go

2.多個協程的效果

為了看到併發效果,這裡舉個最簡單的例子

import (
    "fmt"
    "time"
)

func mygo(name string) {
    for i := 0; i < 10; i++ {
        fmt.Printf("In goroutine %s\n", name)
        // 為了避免第一個協程執行過快,觀察不到併發的效果,加個休眠
        time.Sleep(10 * time.Millisecond) 
    }
}

func main() {
    go mygo("協程1號") // 第一個協程
    go mygo("協程2號") // 第二個協程
    time.Sleep(time.Second)
}

輸出如下,可以觀察到兩個協程就如兩個執行緒一樣,併發執行

In goroutine 協程2號
In goroutine 協程1號
In goroutine 協程1號
In goroutine 協程2號
In goroutine 協程2號
In goroutine 協程1號
In goroutine 協程1號
In goroutine 協程2號
In goroutine 協程1號
In goroutine 協程2號
In goroutine 協程1號
In goroutine 協程2號
In goroutine 協程1號
In goroutine 協程2號
In goroutine 協程1號
In goroutine 協程2號
In goroutine 協程1號
In goroutine 協程2號
In goroutine 協程1號
In goroutine 協程2號

import"fmt"

funcmytest(){
fmt.Println("hello,go")
}

funcmain(){
//啟動一個協程
gomytest()
fmt.Println("hello,world")
}