go通过channel控制并发任务

1. 通过context来控制全体退出

在主协程中创建一个父context,之后的子协程都使用父context生成子context并传递过去

  • context.WithCancelCause(contextFather)

生成子context

  • context.Done()

是一个channel,如果该context或者是父级的context执行了cancel(也可以理解为死掉了),那就会在这个channel中接收到东西,然后可以用于判断context是否退出了

  • 示例
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
//获得子context
func main() {
contextChild, _ := context.WithCancelCause(contextFather)
go heart(contextChild, cancelFather)
go func1(contextChild, cancelFather)
select {
//阻塞等待任务都完成
case <-contextFather.Done():
time.Sleep(3 * time.Second)
return
}
}

//最好有个心跳任务,断连就触发cancel
func heart(ctx context.Context, cancel context.CancelCauseFunc) {
for {
select {
//心跳任务发现用户断开,主动cancel 或者 任务出现error,主动cancel
case <-ctx.Done():
return
default:
(具体心跳逻辑)
}
}
}

func func1(ctx context.Context, cancel context.CancelCauseFunc) {
for {
select {
//心跳任务发现用户断开,主动cancel 或者 任务出现error,主动cancel
case <-ctx.Done():
return
default:
(具体任务内容)
}
}
}

2. 通过channel并发输出resp

  • 通过参数传递同一个channel变量,实现多协程使用同一channel
  • 进阶上面的context控制并发,再创一个输出resp任务
1
2
3
4
5
6
7
8
9
10
11
func writeMessage(ctx context.Context, cancel context.CancelCauseFunc, resp chan []byte) {
for {
select {
//心跳任务发现用户断开,主动cancel 或者 任务出现error,主动cancel
case <-ctx.Done():
return
case data := <-resp:
(具体输出逻辑)
}
}
}