Golang 之 協程 goroutine(三)
阿新 • • 發佈:2018-11-12
“通道” 是連線多個 Go 協程的管道。 可以從一個 Go 協程, 將值傳送到通道,然後在別的 Go 協程中接收。
package main
import "fmt"
func main() {
// 使用 `make(chan val-type)` 建立一個新的通道。
// 通道型別就 val-type 是他們需要傳遞值的型別。
// 此處建立名為 message 的通道
messages := make(chan string)
// 使用 `channel <-` 語法 _傳送_ 一個值到通道中。這裡
// 我們在一個新的 Go 協程中傳送 `"hello channel"` 到上面建立的`messages` 通道中。
go func() { messages <- "hello channel" }()
// 使用 `<-channel` 語法從通道中 _接收_ 一個值。這裡
// 將接收我們在上面傳送的 `"hello channel"` 訊息並打印出來。
// 傳送和接收操作是阻塞的,直到傳送方和接收方都準備完畢。
// 這個特性允許,不使用任何其它的同步操作,來在程式結尾等待
// 訊息 `"hello channel"`
msg := <-messages
fmt.Println(msg)
}
死鎖問題: messages <- “hello channel” 與 msg := <-messages 必須同時進行,不然會出現死鎖
package main
import "fmt"
func main() {
messages := make(chan string)
// 此時已經阻塞在 messages <- "hello channel"
// 解決方案 1. 在messages <- "hello channel"之前,加協程去消費該通道,或者在消費通道之前加協程往通道傳送
// 解決方案 2. 給messages通道加緩衝防止阻塞
messages <- "hello channel"
msg := <-messages
fmt.Println(msg)
}
// 執行結果
//fatal error: all goroutines are asleep - deadlock!
//goroutine 1 [chan send]:
//main.main()
在準備傳送往通道傳送資料之前,開啟協程去消費通道
package main
import (
"fmt"
"time"
)
func f1(msg chan string) {
fmt.Println(<-msg)
}
func main() {
messages := make(chan string)
// 此時已經阻塞在 messages <- "hello channel"
// 解決方案 1. 在messages <- "hello channel"之前,加協程去消費該通道
go f1(messages)
messages <- "hello channel"
// 防止協程在執行途中,因主執行緒的結束而終止
time.Sleep(time.Second * 3)
}
在消費通道之前加協程往通道傳送 messages <- “hello channel”
package main
import (
"fmt"
"time"
)
func main() {
messages := make(chan string)
// 此時已經阻塞在 messages <- "hello channel"
// 解決方案 1. 在消費通道之前加協程往通道傳送 messages <- "hello channel"
go func() {messages <- "hello channel"}()
fmt.Println( <-messages )
// 防止協程在執行途中,因主執行緒的結束而終止
time.Sleep(time.Second * 3)
}
通道緩衝 ,給messages通道加緩衝防止阻塞,通道是 無緩衝 的,這意味著只有在對應的接收(
<- chan
),通道準備好接收時,才允許進行傳送(chan <-
)。可快取通道,允許在沒有對應接收方的情況下,快取限定數量的值。
package main
import (
"fmt"
"time"
)
func main() {
// 最多允許快取一個,此時多傳送和多接收,都會引起死鎖,所以要保證傳送一個,列印一個
messages := make(chan string, 1)
// 此時已經阻塞在 messages <- "hello channel"
// 解決方案 2. 給messages通道加緩衝防止阻塞
messages <- "hello channel"
msg := <-messages
fmt.Println(msg)
// 防止協程在執行途中,因主執行緒的結束而終止
time.Sleep(time.Second * 3)
}
通道同步: 使用通道來同步 Go 協程間的執行狀態。這裡是一個使用阻塞的接受方式來等待一個 Go 協程的執行結束。
package main
import "fmt"
import "time"
// 這是將要在 Go 協程中執行的函式。
// `done` 通道將被用於通知其他 Go 協程這個函式已經工作完畢。
func worker(done chan bool) {
fmt.Print("working...")
time.Sleep(time.Second)
fmt.Println("done")
// 傳送一個值來通知已經完工。
done <- true
}
func main() {
// 執行一個 worker Go協程,並給予用於通知的通道。
done := make(chan bool, 1)
go worker(done)
// 程式將在接收到通道中 worker 發出的通知前一直阻塞。
fmt.Println("阻塞中")
<-done
}