goroutine停止介绍
goroutine是Go语言实现并发编程的利器,简单的一个指令go function就能启动一个goroutine;
但是,Go语言并没有提供终止goroutine的接口,也就是说,我们不能从外部去停止一个goroutine,
只能由goroutine内部退出(main函数终止除外);
我们有很多情况下需要主动关闭goroutine,如需要实现一个系统自动熔断的功能就需要主动关闭goroutine;
第一种方式:
采用for-range从channel上接收值,直到channel关闭,该循环将失效自动推出for range
func TestNameChannel(t *testing.T) {
WaitInfo.Add(1)
exitChannel := make(chan int, 20)
go DoSome1(exitChannel)
for i := 0; i < 10; i++ {
//执行完某个业务的时候决定退出逻辑
exitChannel <- i
}
//如果不关闭通道会导致程序阻塞
close(exitChannel)
WaitInfo.Wait()
}
func DoSome(exitChannel chan int) {
//doSome.....
defer WaitInfo.Done()
for value := range exitChannel {
fmt.Println(value)
}
}
第二种方式:
采用 for select 配合退出队列的队列的方式完成协程的推出
func TestNameChannel(t *testing.T) {
exitChannel := make(chan int)
doSomeChannel := make(chan int)
WaitInfo.Add(1)
go DoSome(exitChannel, doSomeChannel)
for i := 0; i < 3; i++ {
doSomeChannel <- i
time.Sleep(time.Second)
if i == 2 {
exitChannel <- 1
break
}
}
WaitInfo.Wait()
}
func DoSome(exitChannel chan int, doSomeChannel chan int) {
for {
select {
case <-exitChannel:
fmt.Println("我要关闭通道了")
time.Sleep(time.Second)
WaitInfo.Done()
return
case info := <-doSomeChannel:
fmt.Println(info)
time.Sleep(time.Second)
default:
time.Sleep(time.Second)
fmt.Println("===default===")
}
}
}
第三种方式
通过框架提供的Context对象完成协程的优雅推出,Done会返回一个channel,当该context被取消的时候,该channel会被关闭,同时对应的使用该context的routine也应该结束并返回。
var WaitInfo sync.WaitGroup
func TestNameChannel(t *testing.T) {
doSomeChannel := make(chan int)
WaitInfo.Add(1)
ctx, CancelFunc := context.WithCancel(context.Background())
go DoSome(ctx, doSomeChannel)
for i := 0; i < 3; i++ {
doSomeChannel <- i
time.Sleep(time.Second)
if i == 2 {
CancelFunc()
break
}
}
WaitInfo.Wait()
}
func DoSome(context context.Context, doSomeChannel chan int) {
for {
select {
case <-context.Done():
fmt.Println("通过Context对象完成协程的退出")
WaitInfo.Done()
return
case info := <-doSomeChannel:
fmt.Println(info)
time.Sleep(time.Second)
default:
time.Sleep(time.Second)
fmt.Println("===default===")
}
}
}