1、WaitGroup
它是一种控制并发的方式,它的这种方式是控制多个goroutine同时完成。
func main() { var wg sync.WaitGroup wg.Add(2) go func() { time.Sleep(2*time.Second) fmt.Println("1号完成") wg.Done() }() go func() { time.Sleep(2*time.Second) fmt.Println("2号完成") wg.Done() }() wg.Wait() fmt.Println("好了,大家都干完了,放工") }
一个很简单的例子,一定要例子中的2个goroutine同时做完,才算是完成,先做好的就要等着其他未完成的,所有的goroutine要都全部完成才可以。
2、chan通知
我们都知道一个goroutine启动后,我们是无法控制他的,大部分情况是等待它自己结束,那么如果这个goroutine是一个不会自己结束的后台goroutine呢?比如监控等,会一直运行的。
这种情况化,一直傻瓜式的办法是全局变量,其他地方通过修改这个变量完成结束通知,然后后台goroutine不停的检查这个变量,如果发现被通知关闭了,就自我结束。
这种方式也可以,但是首先我们要保证这个变量在多线程下的安全,基于此,有一种更好的方式:chan + select 。
func main() { stop := make(chan bool) go func() { for { select { case <-stop: fmt.Println("监控退出,停止了...") return default: fmt.Println("goroutine监控中...") time.Sleep(2 * time.Second) } } }() time.Sleep(10 * time.Second) fmt.Println("可以了,通知监控停止") stop<- true //为了检测监控过是否停止,如果没有监控输出,就表示停止了 time.Sleep(5 * time.Second) }
3、WithTimeout 超时自动取消方法
当执行一个go 协程时,超时自动取消协程
// 模拟一个最小执行时间的阻塞函数 func inc(a int) int { res := a + 1 // 虽然我只做了一次简单的 +1 的运算, time.Sleep(1 * time.Second) // 但是由于我的机器指令集中没有这条指令, // 所以在我执行了 1000000000 条机器指令, 续了 1s 之后, 我才终于得到结果。B) return res } // 向外部提供的阻塞接口 // 计算 a + b, 注意 a, b 均不能为负 // 如果计算被中断, 则返回 -1 func Add(ctx context.Context, a, b int) int { res := 0 for i := 0; i < a; i++ { res = inc(res) select { case <-ctx.Done(): return -1 default: } } for i := 0; i < b; i++ { res = inc(res) select { case <-ctx.Done(): return -1 default: } } return res }
计算 a+b
func main() { // 使用开放的 API 计算 a+b a := 1 b := 2 timeout := 2 * time.Second ctx, _ := context.WithTimeout(context.Background(), timeout) res := Add(ctx, 1, 2) fmt.Printf("Compute: %d+%d, result: %d ", a, b, res) }
输出结果:Compute: 1+2, result: -1
4、WithCancel 手动取消方法
func main() { // 手动取消 a := 1 b := 2 ctx, cancel := context.WithCancel(context.Background()) go func() { time.Sleep(2 * time.Second) cancel() // 在调用处主动取消 }() res := Add(ctx, 1, 2) fmt.Printf("Compute: %d+%d, result: %d ", a, b, res) }
输出结果:Compute: 1+2, result: -1