可以取消的管線
本頁內容 以這篇為來源的筆記 2 正文連到這篇 5
消費者只需要第一個平方。取到後,它通知 producer 停止,並等待收尾。
package main
import (
"context"
"fmt"
)
func squares(ctx context.Context) (<-chan int, <-chan struct{}) {
out := make(chan int)
done := make(chan struct{})
go func() {
defer close(done)
defer close(out)
for _, n := range []int{1, 2, 3} {
select {
case out <- n * n:
case <-ctx.Done():
return
}
}
}()
return out, done
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
values, done := squares(ctx)
fmt.Println(<-values)
cancel()
<-done
fmt.Println("stopped")
}
儲存為 main.go,執行 go run main.go。Go 1.27.0 的輸出:
1
stopped
兩個訊號,各管一件事
ctx.Done() 通知 producer 取消;done 關閉則表示 producer 已完成收尾。cancel() 本身不等待工作結束。
消費者不再接收時,send 無法繼續。取消訊號讓 select 走到 return,接著執行 defer:先關閉 out,再關閉 done。
誰關閉 channel
producer 是 out 的唯一發送者,由它關閉最容易確認不會再有 send。消費者直接關閉 out,反而可能讓 producer 向已關閉的 channel 發送而 panic。 ^owner
取消沒有優先權
若接收與取消同時可行,select 可能選中 send。本例取走第一個值後就不再接收,因此取消能解除後續 send 的阻塞。
這個模式只涵蓋 channel 等待;計算或 I/O 也必須提供自己的取消方式。1
-
context.CancelFunc(另開分頁) 說明取消不等待工作停止;Go pipelines(另開分頁) 討論下游提早離開時,上游的退出責任。 ↩︎