- 主函数也是线程,在不使用sync.WaitGroup的情况下,需要main等待goroutine完成,不然main完成就没了。
- sync是synchronizing(使) 同步 的缩写
- wg.done()最好加defer
- chan需要make(chan int,5)
- chan记得关闭
- 多协程调度记得使用多路复用,select
- chan无缓冲时,发送阻塞直到数据被接收,接收阻塞直到读到数据
- chan有缓冲时,当缓冲满时发送阻塞,当缓冲空时接收阻塞
- 简而言之,不带缓冲的chan是同步的,必须有人接收才会发送,不然永远阻塞。也必须有人发送才会接收,不然也堵着。
- 带缓冲的chan是异步的,除非缓存满了或者空了,不然不会触发发送阻塞或接受阻塞。
- channel可以返回两个参数,第一个是内容,第二个是ok布尔类型表示通道运行状态。通道关闭返回false
go func() {
// in for-select using ok to exit goroutine
for {
select {
case x, ok := <-in:
if !ok {
return
}
fmt.Printf("Process %d\n", x)
processedCnt++
case <-t.C:
fmt.Printf("Working, processedCnt = %d\n", processedCnt)
}
}
}()
- for-range 是上述ok的简化版 (优雅关闭)
go func(in <-chan int) {
// Using for-range to exit goroutine
// range has the ability to detect the close/end of a channel
for x := range in {
fmt.Printf("Process %d\n", x)
}
}(inCh)
- close(channel)虽然源码看不了,可以认为该函数发送了一个不可再写的内容给了通道。所以下面的写法也是成立的,不会因为通道关闭而引发panic。(优雅关闭)
func worker(stopCh <-chan struct{}) {
go func() {
defer fmt.Println("worker exit")
t := time.NewTicker(time.Millisecond)
for {
select {
case <-t.C:
fmt.Println("Working .")
case <-stopCh:
fmt.Println("Recv stop signal")
return
}
}
}()
return
}
func main() {
stopCh := make(chan struct{})
worker(stopCh)
time.Sleep(time.Second)
close(stopCh)
time.Sleep(time.Second)
fmt.Println("main exit")
}
// Working .
// Working .
// Working .
// Working .
// Recv stop signal
// worker exit
// main exit
- 使用额外的管道信号通知goroutine关闭,主协程通知副协程,副协程等待主协程完成信号
- 或者使用WaitGroup关闭,副协程通知主协程,主协程等待副协程完成信号