相信大家学习go后就或多或少的都看过ctx变量,那么这个ctx到底是干嘛的呢,跟进去发现是Content类型,这里就先浅浅的分析下Content是干嘛的吧
一:结构
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
上面就是Context的结构,一共有四个成员变量
- Deadline 方法返回当前Context被取消的时间,也就是完成工作的截止时间(deadline);
- Done方法需要返回一个channel,这个Channel会在当前工作完成或者上下文被取消之后关闭,可以在子goroutine中利用select进行监控,来回收子goroutine;多次调用Done方法会返回同一个Channel;
- Err方法会返回当前Context结束的原因,它只会在Done返回的Channel被关闭时才会返回空值:
- 如果当前Context被取消就会返回Canceled错误;
- 如果当前Context超时就会返回DeadlineExceeded错误;
- Value 方法会从Context中返回键对应的值,对于同一个上下文来说,多次调用Value并传入相同的Key会返回相同的结果,该方法仅用于传递跨API和进程间跟请求域的数据。
二:使用场景
Content中文是叫上下文,这个名字有点不是很直观,不知道具体是干嘛的,下面就统计了Content的几个使用场景
1.上下文信息传递,类似中间件(java中的拦截器,过滤器)将处理的信息存起来传给后面的方法
2.精准控制goroutine的运行 (控制孙子类的协程,通知别的协程之类的)
三:上下文传递
实现上下文传递靠的是Value(key interface{})成员变量
示例
ctx := context.Background()
ctx = context.WithValue(ctx, "k1", "1")
ctx = context.WithValue(ctx, "k2", "2")
注意每次存一个value就会再生成一个子content,所以查找的时候会层层往上查,比如要查k1的值,会先查到k2对应的Content,k2中没有就会往父类k1中去找
四:精准控制goroutine
除了能将信息传给后面调用的方法,Content还有个更重要的属性就是控制goroutine,我们都知道go的协程很容易开启,所以就要有个精准的关闭来控制它防止因为协程未关闭发生协程泄露
示例
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context) {
for {
select {
case <- ctx.Done():
fmt.Println("cancel goroutine by context!")
return
default:
fmt.Println("I'm alive")
time.Sleep(1 * time.Second)
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
go worker(ctx)
time.Sleep(time.Second * 4)
cancel() // 通知子goroutine结束
fmt.Println("over")
}
上面通过context自带的WithCancel方法将cancel函数传递出来,然后手动调用cancel()函数给goroutine传递了ctx.Done()信号。这样就能精准控制协程的释放了