context
为什么有context
-
首先,如果我们在并发程序中,如果需要我们去通知子协程结束我们会怎么做?
-
我们可能会通过一个channel+select去通知,如下:
package main import ( "fmt" "time" ) func main() { exitChan := make(chan bool) go func() { for { fmt.Println("Doing Work......") select { case <-exitChan: return } } }() time.Sleep(time.Second * 1) fmt.Println("stop the goRoutinue") close(exitChan) time.Sleep(time.Second * 3) }
-
这种做法能传递我们需要给子协程的信号,但是他是有限的,比如如果我想在指定的时间间隔内通知,想传递为什么取消的信息,如果需要控制子协程以及子协程的子协程,多个层级下使用exit Channel的方式会变得混乱复杂,所以官方就为goroutine控制开发了context包
什么是context
-
context是协程并发安全的
-
context包可以从已有的context实例派生新的context,形成context的树状结构,只要一个context取消了,派生出来的context将都会被取消
创建context树:
-
第一步创建根结点, 使用emptyCtx(int类型变量)…context.Background经常用做context树的根结点,由接收请求的第一个routine创建,不能被取消,没有值也没有过期时间
type emptyCtx int func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { return } func (*emptyCtx) Done() <-chan struct{ } { return nil } func (*emptyCtx) Err() error { return nil } func (*emptyCtx) Value(key interface{ }) interface{ } { return nil } // emptyCtx不能存储额外信息,没有超时时间,不能取消,都是nil // Background returns a non-nil, empty Context. It is never canceled, has no // values, and has no deadline. It is typically used by the main function, // initialization, and tests, and as the top-level Context for incoming // requests. func Background() Context { return background } // TODO returns a non-nil, empty Context. Code should use context.TODO when // it's unclear which Context to use or it is not yet available (because the // surrounding function has not yet been extended to accept a Context // parameter). func TODO() Context { return todo }
通过注释,可以看到context.Background返回一个非空的context,通常由主函数,初始化和测试使用,并作为传入请求的top-level Context (顶级上下文)。TODO也是返回一个非空的context,当不知道应该使用context时使用TODO…两者只是使用场景不同,代码实现都是一样
-
创建子孙节点由四个函数
- func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
// 对应cancelCtx(参考文末的structure图) // 返回ctx和cancel函数,可以让后代的goroutine退出,关闭对应的c.done // 创建了可取消的cancelCtx类型 =》 对应下面的cancelCtx func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { c := newCancelCtx(parent) propagateCancel(parent, &c) return &c, func() { c.cancel(true, Canceled) } } // newCancelCtx returns an initialized cancelCtx.,生成新的子节点 func newCancelCtx(parent Context) cancelCtx { return cancelCtx{ Context: parent} } // propagateCancel arranges