一.在Go语言中有两种较为优雅的方式退出goroutine
1.使用自定义channel通知goroutine退出。
2.使用context传递上下文通知goroutine退出。(官方推荐;本质上也是通过channel通知)
自定义channel版本:
package main
import (
"fmt"
"time"
)
//如何优雅地退出goroutine
//1.使用自定义channel版本
func worker(exit_ch chan struct{}){
for{
fmt.Println("worker")
time.Sleep(time.Second*1)
select{
case <-exit_ch:
return
default:
}
}
}
func main() {
exit_ch := make(chan struct{})
go worker(exit_ch)
time.Sleep(time.Second*5)
exit_ch<-struct{}{}
close(exit_ch)
fmt.Println("over")
}
官方版本:
官方推荐版本,明显是优于自定义channel版本,因为官方context里面已经统一为我们定义了channel;更重要的是,使用context我们可以取消多个goroutine,下面代码中我们在worker中新开了一个worker2的goruntine,调用cancel也会通知worker2退出。
package main
import "fmt"
import "time"
import "context"
func worker(ctx context.Context) {
go worker2(ctx)//在子go中再开一个go,传入ctx
LOOP:
for {
fmt.Println("worker")
time.Sleep(time.Second)
select {
case <-ctx.Done(): // 等待上级通知
break LOOP
default:
}
}
}
//主线程调用cancle(),worker2也能收到通知退出。
func worker2(ctx context.Context){
LOOP:
for {
fmt.Println("worker2")
time.Sleep(time.Second)
select {
case <-ctx.Done(): // 等待cancel通知
break LOOP//跳出外层循环
default:
}
}
}
func main() {
ctx,cancel := context.WithCancel(context.Background())
go worker(ctx)
time.Sleep(time.Second*5)
//通知子goroutine
cancel()
fmt.Println("cancel")
time.Sleep(time.Second*5)
fmt.Println("over")
}