n := <-ch // 当 ch 被关闭后,n 将被赋值为 ch 元素类型的零值 m, ok := <-ch // 当 ch 被关闭后,m 将被赋值为 ch 元素类型的零值,ok 将被赋值为 false for v := range ch { // 当 ch 被关闭后,循环会自动结束 }
向一个已经关闭的 channel 执行发送操作,会引发 panic
1 2 3 4
ch := make(chanint, 1)
close(ch) ch <- 1// panic: send on closed channel
但从一个已经关闭的 channel 执行接收操作,不会发生 panic,但会得到一个类型零值
select
同时在多个 channel 上进行发送或者接收操作
1 2 3 4 5 6 7 8 9 10
select { case x := <-ch1: // 从 ch1 接收数据,即便 ch1 已经关闭,此时会得到一个类型零值 ... case y, ok := <-ch2: // 从 ch2 接收数据,并根据 ok 判断是否关闭 ... case ch3 <- z: // 将 z 发送到 ch3 ... default: ... }
funcworker(i int) { fmt.Printf("worker %d: is working\n", i) time.Sleep(1 * time.Second) fmt.Printf("worker %d: works done\n", i) }
funcspawnGroup(f func(i int), num int, groupSignal <-chan signal) <-chan signal { c := make(chan signal) var wg sync.WaitGroup
for i := 0; i < num; i++ { wg.Add(1) gofunc(i int) { defer wg.Done()
if v, ok := <-groupSignal; !ok { // 阻塞在 groupSignal,等待关闭信号 fmt.Printf("spawnGroup: worker %d: group signal is closed, v: %v\n", i, v) }
fmt.Printf("spawnGroup: worker %d: start to work\n", i) f(i) }(i) }
gofunc() { wg.Wait() c <- signal{} }()
return c }
funcmain() { fmt.Println("main: start a group of workers") groupSignal := make(chan signal) c := spawnGroup(worker, 5, groupSignal) time.Sleep(5 * time.Second)
fmt.Println("main: the group of workers start to work") close(groupSignal) // 向所有 worker goroutine 广播关闭信号 <-c fmt.Println("main: the group of workers works done") }
// Output: // main: start a group of workers // main: the group of workers start to work // spawnGroup: worker 1: group signal is closed, v: {} // spawnGroup: worker 1: start to work // worker 1: is working // spawnGroup: worker 2: group signal is closed, v: {} // spawnGroup: worker 2: start to work // worker 2: is working // spawnGroup: worker 3: group signal is closed, v: {} // spawnGroup: worker 3: start to work // worker 3: is working // spawnGroup: worker 4: group signal is closed, v: {} // spawnGroup: worker 4: start to work // worker 4: is working // spawnGroup: worker 0: group signal is closed, v: {} // spawnGroup: worker 0: start to work // worker 0: is working // worker 2: works done // worker 3: works done // worker 4: works done // worker 1: works done // worker 0: works done // main: the group of workers works done
functrySend(c chan<- int, i int)bool { select { case c <- i: returntrue default: // channel 满 returnfalse } }
functryReceive(c <-chanint) (int, bool) { select { case i := <-c: return i, true default: // channel 空 return0, false } }
funcproducer(c chan<- int) { i := 1 for { time.Sleep((1 << 1) * time.Second) if ok := trySend(c, i); ok { fmt.Printf("producer: send %d to channel\n", i) i++ continue } fmt.Printf("producer: try to send %d to channel, but channel is full\n", i) } }
funcconsumer(c <-chanint) { for { i, ok := tryReceive(c) if !ok { fmt.Printf("consumer: try to receive from channel, but channel is empty\n") time.Sleep(1 * time.Second) continue }
fmt.Printf("consumer: receive %d from channel\n", i) if i >= 3 { fmt.Printf("consumer: exit\n") return } } }
funcmain() { var wg sync.WaitGroup
c := make(chanint, 3) wg.Add(2)
gofunc() { defer wg.Done() producer(c) }()
gofunc() { defer wg.Done() consumer(c) }()
wg.Wait() }
// Output: // consumer: try to receive from channel, but channel is empty // consumer: try to receive from channel, but channel is empty // consumer: try to receive from channel, but channel is empty // producer: send 1 to channel // consumer: receive 1 from channel // consumer: try to receive from channel, but channel is empty // producer: send 2 to channel // consumer: receive 2 from channel // consumer: try to receive from channel, but channel is empty // consumer: try to receive from channel, but channel is empty // producer: send 3 to channel // consumer: receive 3 from channel // consumer: exit // producer: send 4 to channel // producer: send 5 to channel // producer: send 6 to channel // producer: try to send 7 to channel, but channel is full // producer: try to send 7 to channel, but channel is full // producer: try to send 7 to channel, but channel is full // producer: try to send 7 to channel, but channel is full // producer: try to send 7 to channel, but channel is full // producer: try to send 7 to channel, but channel is full
约束:只有在 channel 状态发生变化时,才能探测到
特定场景下(单向无竞争),可以利用 len 来实现
nil channel
如果一个 channel 类型变量的值为 nil,即 nil channel,对其的读写都会阻塞
1 2 3 4 5 6 7
funcmain() { var c chanint println(c) // 0x0 println(c == nil) // true
<-c // fatal error: all goroutines are asleep - deadlock! }
1 2 3 4 5 6 7
funcmain() { var c chanint println(c) // 0x0 println(c == nil) // true
c <- 1// fatal error: all goroutines are asleep - deadlock! }
for { select { case v, ok := <-ch1: if ok { fmt.Println(v) } else { ch1 = nil// 探测到 channel 被关闭,将其置为 nil,读写都会阻塞,下次 select 时将不会再选中该 case } case v, ok := <-ch2: if ok { fmt.Println(v) } else { ch2 = nil } }
if ch1 == nil && ch2 == nil { break }
} fmt.Println("Done") }
// Output: // 5 // 7 // Done
select
default
参照上述 trySend 和 tryReceive
无论是无缓冲 channel 还是带缓冲 channel 都适用
time/sleep.go
1 2 3 4 5 6 7
funcsendTime(c interface{}, seq uintptr) { // 无阻塞地向 c 发送当前时间 select { case c.(chan Time) <- Now(): default: } }
超时
1 2 3 4 5 6 7 8 9 10
var c chanint
funcworker() { select { case <-c: // do something case <-time.After(30 * time.Second): return } }
time/sleep.go
1 2 3
funcAfter(d Duration) <-chan Time { return NewTimer(d).C }
timer 是由 Go Runtime 维护的,而不是操作系统级的定时器资源,使用代价要低很多
避免因为使用 Timer 而给 Go Runtime 和 Go GC 带来压力,要及时调用 Timer 的 Stop 方法来回收 Timer 资源