方式一:Sleep
package main
import "time"
func main() {
go func() {}()
time.Sleep(time.Second)
}
这种方式,主协程为了等待子协程结束,阻塞了1s时间。
好处是:主协程什么时候解除阻塞,不依赖子协程
缺点是:主协程需要等待多久不好确定
方案二:WaitGroup
func main() {
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
// do something
//如果忘记这个的话,主协程永远阻塞,runtime判断没事可错了,不如直接死锁panic
wg.Done()
}()
wg.Wait()
}
这种方式,主协程的解除阻塞条件依赖于子协程
方案三:Channel
func main() {
// 零缓冲channel
finish := make(chan struct{})
go func() {
// do something
//如果忘记这个的话,主协程永远阻塞,runtime判断没事可错了,不如直接死锁panic
finish <- struct{}{}
}()
<-finish
}
这种方式依赖于读取无缓冲通道会阻塞的特性,主协程依赖于子协程
方案四:死循环
package main
import "time"
func main() {
go func() {
// do something
}()
for {
time.Sleep(time.Second)
}
}
死循环里必须sleep,不然的话比较消耗cpu。for {}
是一个 忙等待(busy loop),会导致一个逻辑 CPU 核心持续满载(即使 goroutine 里没有实际工作)
方案五:Select
package main
import "time"
func main() {
go func() {
// do something
// 如果没有一直执行的话,主协程永远阻塞,runtime判断没事可错了,不如直接死锁panic
for {
time.Sleep(time.Second)
}
}()
select {}
}
通过 select{}
来永久阻塞,但 不消耗 CPU,同时允许其他 goroutine 执行。
方案六:锁
package main
import (
"sync"
"time"
)
func main() {
mutex := sync.Mutex{}
// 第一次顺利抢到锁
mutex.Lock()
go func() {
// do something
time.Sleep(5 * time.Second)
//如果忘记这个的话,主协程永远阻塞,runtime判断没事可错了,不如直接死锁panic
mutex.Unlock() // 锁释放
}()
// 第二次拿不到锁,等待锁释放
mutex.Lock()
}