context在go中被称作上下文,主要是用来控制go中生成的goroutine,当goroutine少的时候还好控制,当goroutine一生百,百生千,这时候该怎么管理呢,于是context就在这里派上了用场。
func main() {
stop := make(chan bool)
go func() {
for {
select {
case <-stop:
fmt.Println("监控退出,停止了...")
return
default:
fmt.Println("goroutine监控中...")
time.Sleep(2 * time.Second)
}
}
}()
time.Sleep(10 * time.Second)
fmt.Println("可以了,通知监控停止")
stop<- true
//为了检测监控过是否停止,如果没有监控输出,就表示停止了
time.Sleep(5 * time.Second)
}
上面的例子是用channel来控制goroutine,但是如果goroutine再生成新的goroutine,我们就要建更多channel,代码量也会更多,内存消耗也比较大,这时候就体现了context的优势了,下面看下context的例子。
func main() {
ctx, cancel := context.WithCancel(context.Background())
go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("监控退出,停止了...")
return
default:
fmt.Println("goroutine监控中...")
time.Sleep(2 * time.Second)
}
}
}(ctx)
time.Sleep(10 * time.Second)
fmt.Println("可以了,通知监控停止")
cancel()
//为了检测监控过是否停止,如果没有监控输出,就表示停止了
time.Sleep(5 * time.Second)
}
主要就是用channel换成context,似乎看起来跟使用channel差不多嘛,那是因为举的例子只用了一个goroutine,如果是多个比如几千,几万个呢,goroutine再生成goroutine,这样我们光是在channel的判断上就要耗费很多时间。我们先简单看下上面例子的代码
1、context.Background(),这个作用就是相当于注册一个context根节点,以后我们通过context.WithCancel获得的context都是注册在这根节点上面的。
2、context.WithCancel 这个就是获得一个子Context,同时得到cancel方法,这个在后面用到
3、 将第二步得到的Context作为上下文传递到goroutine里,同时在goroutine里监听context.Done() 的信号,这个Done信号是什么时候传递的呢,看第四步
4、第二步我们通过WithCancel得到的cancel方法,在主函数中调用,这个时候,第三步中context.Done()就得到了信号触发。于是goroutine退出,从而实现了控制goroutine。
整体流程就是这样,方法也比较简单。下面我们看下context接口实现
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
这个接口共有4个方法,了解这些方法的意思非常重要,这样我们才可以更好的使用他们。
Deadline方法是获取设置的截止时间的意思,第一个返回式是截止时间,到了这个时间点,Context会自动发起取消请求;第二个返回值ok==false时表示没有设置截止时间,如果需要取消的话,需要调用取消函数进行取消。
Done方法返回一个只读的chan,类型为struct{},我们在goroutine中,如果该方法返回的chan可以读取,则意味着parent context已经发起了取消请求,我们通过Done方法收到这个信号后,就应该做清理操作,然后退出goroutine,释放资源。
Err方法返回取消的错误原因,因为什么Context被取消。
Value方法获取该Context上绑定的值,是一个键值对,所以要通过一个Key才可以获取对应的值,这个值一般是线程安全的。可以通过这个方法进行上下文之间的传值操作
上面都是接口指定方法,context包提供了常用的几个方法
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
除了第一个已经在示例中用到了,其他几个都是依据context接口里面的方法实现,用法可自行去了解。
context包分析完了,接下来对于net包来进行分析,net包比较大,需要用多个篇幅来介绍。