var wg sync.WaitGroup func foo(){ defer wg.Done() for { fmt.Println("我是foo函数内的Print") time.Sleep(time.Millisecond*500) } } func main() { wg.Add(1) go foo() wg.Wait() }
以上代码永远不会退出,那么如何来通知子goroutine退出呢?
一、可以添加一个标记值
// 添加tag标记值 var wg sync.WaitGroup var tag bool // 标记值 func foo(){ defer wg.Done() for { fmt.Println("我是foo函数内的Print") time.Sleep(time.Millisecond*500) if tag { // tag为真退出foo fmt.Printf("foo停止啦") break } } } func tagFunc(tag *bool){ time.Sleep(time.Second*10) *tag = true // 将tag设为真 } func main() { wg.Add(1) go foo() go tagFunc(&tag) wg.Wait() }
二、channel传值
var wg sync.WaitGroup var tag = make(chan bool,1) // 初始化tag channel func foo(){ defer wg.Done() FOO://for循环标签 for { fmt.Println("我是foo函数内的Print") time.Sleep(time.Millisecond*500) select { case <- tag: fmt.Println("foo停止啦") close(tag) break FOO // 跳出for虚幻 default: } } } func tagFunc(){ time.Sleep(time.Second*10) tag <- true // 向通道传值 wg.Done() } func main() { wg.Add(2) go foo() go tagFunc() wg.Wait() }
三、context(官方推荐)
// 原理与第二种类似 var wg sync.WaitGroup func foo2(ctx context.Context) { defer wg.Done() FOO2: for { fmt.Println("我是foo2函数内的Print") time.Sleep(time.Millisecond * 500) select { case <-ctx.Done():// 有值执行退出 fmt.Println("foo2停止啦") break FOO2 default:// 无值继续循环 } } } func foo(ctx context.Context) { defer wg.Done() wg.Add(1) go foo2(ctx) // 将ctx传递给子goroutine FOO: for { fmt.Println("我是foo函数内的Print") time.Sleep(time.Millisecond * 500) select { case <-ctx.Done(): // 有值执行退出 fmt.Println("foo停止啦") break FOO default: // 无值继续循环 } } } func tagFunc(cancel context.CancelFunc) { time.Sleep(time.Second * 10) cancel() //运行cancelFunc wg.Done() } func main() { // ctx可用于goroutine之间传递,当执行cancelFunc时,ctx.Done()传入空的chan结构体 ctx, cancelFunc := context.WithCancel(context.Background()) wg.Add(2) go foo(ctx) go tagFunc(cancelFunc) wg.Wait() }