佛门广大,岂不容一癫僧!
使用channel控制goroutine
传统的使用channel控制goroutine的结束
1
2
3
4
5
|
ch:=make(chan int)
go func(){
<-ch
}
ch<-1
|
缺点:如果有很多个goroutine需要结束,或者很多嵌套goroutine。使用这种方法就很复杂了。
使用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
|
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, canel := context.WithCancel(context.Background())
//Context.Backgroud(),作为context树结构的顶层。
//有了顶层之后,就可以衍生出更多的子Context.
//
go watch(ctx, "1")
go watch(ctx, "2")
go watch(ctx, "3")
time.Sleep(10 * time.Second)
fmt.Println("要结束了")
canel()
time.Sleep(5 * time.Second)
}
func watch(ctx context.Context, name string) {
for {
select {
case <-ctx.Done():
fmt.Println(name, "stop")
return
default:
fmt.Println(name, "running")
time.Sleep(2 * time.Second)
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
2 running
1 running
3 running
3 running
2 running
1 running
1 running
2 running
3 running
1 running
3 running
2 running
2 running
1 running
3 running
要结束了
1 stop
2 stop
3 stop
|
具体的就是使用ctx替换上文的channel
Context接口
1
2
3
4
5
6
|
type Context interface{
Deadline()(deadline time.Time,ok bool)#获取设置的截止时间,ok为false的话代表没有设置
Done() <-chan struct{}#返回一个只读的channel,读取到chann则代表接受到了退出请求
Err() err#返回取消的原因
Value(key interface{}) interface{}#返回绑定在Context上的值
}
|
Context衍生
context包为我们提供了with系列的函数。
1
2
3
4
|
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context
|
前三个都返回一个取消函数
deadline和timeout分别是到相应的时间点和设置超时时间来取消。
第4个生成绑定了一个键值对的Context.
Context.Value
使用context.Value就是用继承的方法,加上一个withValue,就可以用ctx.Value获取到值。
值必须是可比的,value是线程安全的,一般是值。
1
2
3
4
5
6
|
ctx, cancel := context.WithCancel(context.Background())
//附加值
valueCtx:=context.WithValue(ctx,key,"【监控1】")
go watch(valueCtx)
………………
print(ctx.Value(key))
|
使用
- 不要把Context放在结构体中,要以参数的方式传递
- 以Context作为参数的函数方法,应该把Context作为第一个参数,放在第一位。
- 给一个函数方法传递Context的时候,不要传递nil,如果不知道传递什么,就使用context.TODO
- Context的Value相关方法应该传递必须的数据,不要什么数据都使用这个传递
- Context是线程安全的,可以放心的在多个goroutine中传递