如何优雅的退出子goroutine

Posted by Ethan Blog on January 12, 2021

退出子goroutine的方法很多,可以使用全局变量channel、也可以使用官方提供的context类,下面演示使用官方的context类来优雅的通知子goroutine退出。

代码演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package main

import (
	"context"
	"log"
	"sync"
	"time"
)
var waitgroup sync.WaitGroup

//子goroutine 1
func worker1(ctx context.Context){
	defer  waitgroup.Done()
	go worker2(ctx) //演示再去开一个子goroutine
	BREAK:
	  for{
		  select {
			  case <-ctx.Done():
				break BREAK
			  default:
		  }
		  log.Println("worker1 ...")
		  time.Sleep(1*time.Second)
		}
}

//子goroutine 2
func worker2(ctx context.Context){
BREAK:
	for{
		select {
		case <-ctx.Done():
			break BREAK
		default:
		}
		log.Println("worker2 ...")
		time.Sleep(1*time.Second)
	}
}

func main() {
	//创建一个context,并标记为根节点
	ctx ,cancel := context.WithCancel(context.Background())
	waitgroup.Add(1)
		go worker1(ctx) //启动一个子goroutine
	time.Sleep(time.Second * 5)
	cancel() //context内置方法,用于退出子goroutine
	waitgroup.Wait()
	//演示验证worker是否退出
	for{
		log.Println("over ...")
		time.Sleep(1*time.Second)
	}
}

可以看到,main函数中启了woker1协程,并且woker1协程函数中又启了worker2协程,当5秒后调用了context类中的cancel()方法来通知子goroutine退出,worker1worker2都同时退出。