context的基础接口结构:
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key any) any
}
作用:控制子协程的退出(内部其实还是通过channel的通信来实现,对其进行了封装)
- Deadline() (deadline time.Time, ok bool)
- 返回工作的截止时间. (若没有, 则 ok=false)
- Done() <-chan struct{}
- 返回一个管道. 若从管道中可以读到数据, 说明任务被关闭了. (若不需要, 返回 nil)
- 通常做法为在需要退出时将管道关闭.
- Err() error
- 返回任务被关闭的原因.
- Value(key any) any
- 返回上下文包含的环境变量
WithCancel
从父context中复制出一个带有关闭函数的context,一旦其被调用或者父进程结束,这个子进程就结束了,以下为一个例子,此例子中子进程为一个定时任务,每隔一秒从msg channel中读取接收的信息,主进程将0-4这5个数传入msg channel中
func main() {
ctx, close := context.WithCancel(context.Background())
message := make(chan int)
go son(ctx,message)
for i := 0; i < 5; i++ {
message <- i
}
close()
time.Sleep(2 * time.Second)
fmt.Println("主进程结束")
}
func son(ctx context.Context,msg chan int) {
t := time.Tick(time.Second)
for range t {
select {
case m := <- msg:
fmt.Printf("接收到信息:%d \n",m)
case <- ctx.Done():
fmt.Println("子协程结束")
return
}
}
}
WithDeadline
可以设置一个过期的时间,到时间后context关闭
func main() {
ctx, close := context.WithDeadline(context.Background(), time.Now().Add(time.Second * 2))
message := make(chan int)
go son(ctx,message)
for i := 0; i < 5; i++ {
message <- i
}
defer close()
//time.Sleep(time.Second)
fmt.Println("主进程结束")
}
func son(ctx context.Context,msg chan int) {
t := time.Tick(time.Second)
for range t {
select {
case m := <- msg:
fmt.Printf("接收到信息:%d \n",m)
case <- ctx.Done():
fmt.Println("子协程结束")
return
}
}
}
如图所示在2秒后子协程关闭,由于msg channel中有数据,主线程处于阻塞状态
红圈表示主线程并没有结束,处于阻塞的状态
WithTimeout
可以设置超时时长,超过时长自动关闭,与WithDeadline类似,一个设置超时时长,一个设置特定时间关闭的context
WithValue
在复制出来的新的context中可以携带一个k - v的环境变量,如下例子所示(在withCancel上改进了一下):在此ctx中就可以带有name - panda这一键值对的一个环境变量
func main() {
ctxValue := context.WithValue(context.Background(), "name", "panda")
ctx, close := context.WithCancel(ctxValue)
message := make(chan int)
go son(ctx,message)
for i := 0; i < 5; i++ {
message <- i
}
close()
time.Sleep(2 * time.Second)
fmt.Println("主进程结束")
}
func son(ctx context.Context,msg chan int) {
t := time.Tick(time.Second)
for range t {
select {
case m := <- msg:
fmt.Printf("接收到信息:%d \n",m)
case <- ctx.Done():
fmt.Println("子协程结束",ctx.Value("name"))
return
}
}
}
本文正在参加技术专题18期-聊聊Go语言框架