iT邦幫忙

2021 iThome 鐵人賽

DAY 9
0
自我挑戰組

golang 後端菜雞工程師學習雜記系列 第 12

Day 12 複習 golang concurrency 語法篇 I

  • 分享至 

  • xImage
  •  

Goroutine

在 go 語言裡要使用 goroutine 非常簡單,語法如下,不會像 C 語言使用 thread 有太多複雜的語法。(注意:goroutine 很像 thread 但實際上卻不是 thread,這裡不談,想看 goroutine 和 thread 的差異已經有很多討論)。

go func() {
    doSomething()
}()

通常多個 goroutine 會互相溝通,達到正確執行的結果,剛開始學 gorotuine 時會寫出像以下的程式,然後什麼都沒印出來就結束了,這就是因為 main goroutine 和此 goroutine 沒有溝通的結果:在 go spec 指出,要是 main 執行完了是不會等其他 goroutine 的,也就是說在 main goroutine 執行的這段時間裡,根本來不及等到 scheduler 排程此匿名 goroutine 並且執行,就結束了。

從這可以觀察到:單單只有 goroutine 是不行的,我們需要有工具讓多個 goroutine 互相溝通,這工具其實就是 channel。

// Nothing will print
func main() {
    go func() {
        fmt.Println("Hello world")
    }()
}

預期的寫法應該如下,其實就是多創造了一個 channel,並且兩個不同的 goroutine 做了以下事情

  • 匿名 goroutine:印出 Hello world 之後通知 main goroutine
  • main goroutine:等待匿名 goroutine 的通知
func main() {
    done := make(chan string)
    go func() {
        fmt.Println("Hello world")
        done <- "done"
    }()
    <- done
}

這裡只是先給出一個概念,知道需要 goroutine 需要 channel 才會運作的良好。沒看懂沒關係,之後就看懂了。

Channel

channel 是用來給 goroutine 溝通的。 goroutine 可以對 channel 做兩個操作 send 和 receive。
以下就是建立一個 channel 的語法。 string 型態代表,此 channel 所 send 和 receive 的是一個 string。

ch := make(chan string)

send 操作,將某個值放入 channel

ch <- "hello"

receive 操作,使用某個變數接收 channel 裏的值 (line 1),也可以接收到之後把這個值丟掉 (line 2)

myVar := <- ch
<- ch

接下來我們將會談到,channel 的 block 機制

Unbuffered channel

以下我將會使用幾個名詞:傳送者就是只使用 send 的人,接收者是指使用 receive 操作的人

我們可以把 unbuffered channel 想像成一個掛號信的傳送,信件一定要傳輸到收信人手上,我們分別用傳送者跟接收者的角度來看:
傳送者:目的把信件送到接收者手上,等到接收者出現我才去做其他事情
接收者:目的在從傳送者手上收到信件,等到接收者出現我才去做其他事情

我們剛剛所講的語法其實就是 Unbuffered channel,再看一次:

ch := make(chan string)

Buffered Channel

把 Buffered Channel 想像成一種特殊的平信,信件一定要塞到信箱裡,我們分別用傳送者跟接收者的角度來看:
傳送者:目的把信件送到信箱裡,如果信箱滿了,那就等,等到信有位置了再把信塞進去
接收者:目的在把信件從信箱拿出來,如果信箱空了,那就等,等到有信了再從信箱拿出來

以下就是 buffer 大小為五個 string 的 channel,如果在 channel 沒有人接收的情況下,在 send 一個 string 則會 block

ch := make(chan string, 5)

關閉 channel

我們可以透過 close 語法關閉 channel

ch := make(chan string)
close(ch)

注意:

  • 對於關閉的 channel 做 send 操錯會 runtime panic
  • 關閉已經關閉的 channel 換 runtime panic
  • 對於已關閉的 channel ,做出 receive 操作得話會先接收到 close 之前 send 出的值,在之後接收的值就是 channel 型態的 zero value。可以看下面的例子。
func main() {
	ch := make(chan int, 2)
	go func() {
		ch <- 1
		ch <- 2
		close(ch)
	}()
	fmt.Println(<-ch) // print 1
	fmt.Println(<-ch) // print 2
	fmt.Println(<-ch) // print 0
}

Reference: https://golang.org/ref/spec#Close

Reference


上一篇
Day11 休息一日放假一下
下一篇
Day 13 複習 golang concurrency 語法篇 II
系列文
golang 後端菜雞工程師學習雜記18
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言