通道(Channel)是 goroutine 之間通訊的管道,「以通訊代替共享」的具體載體。
基本用法#
// 建立 channel
ch := make(chan int) // 無緩衝
chBuf := make(chan int, 10) // 有緩衝,容量 10
// 傳送 / 接收
ch <- 42
value := <-ch
// 關閉
close(ch)無緩衝 vs 有緩衝#
// 無緩衝:同步通訊
// 傳送者阻塞,直到有接收者
ch := make(chan int)
go func() {
ch <- 1 // 阻塞,直到有人接收
}()
value := <-ch // 接收後,傳送端才會繼續
// 有緩衝:非同步通訊
// 只在緩衝區滿時,傳送才阻塞
chBuf := make(chan int, 2)
chBuf <- 1 // 不阻塞
chBuf <- 2 // 不阻塞
// chBuf <- 3 // 阻塞,緩衝區已滿
- 無緩衝:需要嚴格同步、握手語意時使用
- 有緩衝:允許傳送端先行、降低等待時間
操作規則#
| 操作 | nil channel | 已關閉 channel | 正常 channel |
|---|---|---|---|
| 傳送 | 永久阻塞 | panic | 阻塞/成功 |
| 接收 | 永久阻塞 | 回傳零值 | 阻塞/成功 |
| 關閉 | panic | panic | 成功 |
ch := make(chan int, 1)
ch <- 10
close(ch)
// 從已關閉的 channel 接收
v1, ok := <-ch // v1=10, ok=true
v2, ok := <-ch // v2=0, ok=false(channel 已空且已關閉)
// 關閉後繼續傳送會 panic
// ch <- 20 // panic: send on closed channel單向 Channel#
// 只能傳送
func producer(out chan<- int) {
for i := 0; i < 10; i++ {
out <- i
}
close(out)
}
// 只能接收
func consumer(in <-chan int) {
for value := range in {
fmt.Println(value)
}
}
func main() {
ch := make(chan int)
go producer(ch) // 雙向 channel 可隱式轉成單向
consumer(ch)
}把方向寫進函式簽章,可以讓「誰負責 close」的責任在編譯期就明確。
select 語句#
select 用於同時監聽多個 channel 操作:
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
time.Sleep(time.Second)
ch1 <- 1
}()
go func() {
time.Sleep(time.Second * 2)
ch2 <- 2
}()
// 等第一個就緒的 channel
select {
case v := <-ch1:
fmt.Println("from ch1:", v)
case v := <-ch2:
fmt.Println("from ch2:", v)
case <-time.After(time.Second * 3):
fmt.Println("timeout")
}
}select 的進階用法
// 非阻塞操作
select {
case v := <-ch:
fmt.Println("received:", v)
default:
fmt.Println("no value available")
}
// 帶 timeout 的傳送
select {
case ch <- value:
fmt.Println("sent")
case <-time.After(time.Second):
fmt.Println("send timeout")
}
// 永久等待
select {} // 永久阻塞