golang的Context

golang的Context,第1张

  为什么需要 Context:在并发程序中,由于超时、取消 *** 作或者一些异常情况,往往需要进行抢占 *** 作或者中断后续 *** 作

  举个例子:在 Go http 包的 Server 中,每一个请求都有一个对应的 goroutine 去处理。请求处理函数通常会启动额外的 goroutine 用来访问后端服务,比如数据库和 RPC 服务,用来处理一个请求的 goroutine 通常需要访问一些与请求特定的数据,比如终端用户的身份认证信息、验证相关的token、请求的截止时间。 当一个请求被取消或超时时,所有用来处理该请求的 goroutine 都应该迅速中断退出,然后系统才能释放这些 goroutine 占用的资源

  说白了就是一个程序可能在主 goroutine 起了一个 子goroutine 来启动对应的服务,这个 子goroutine 里面可能又存在一个 孙goroutine 来进行其他动作,那么如果我现在想取消在主 goroutine 中产生的所有子 goroutine 或者孙 goroutine,那这时就可以使用 context

context 的常用场景 1.一个请求对应多个 goroutine 之间的数据交互 2.超时控制 3.上下文控制

根据官方文档的说法,该类型被设计用来在API 边界之间以及过程之间传递截止时间、取消信号及其他与请求相关的数据
Context 实际上是一个接口,其提供了四个方法

	//Deadline 返回 ctx 的截止时间,ok 为 false 表示没有设置。
	//达到截止时间的 ctx 会被自动 Cancel 掉; 
	Deadline() (deadline time.Time, ok bool)
	
	//如果当前 ctx 是可取消的,Done 返回一个chan 用来监听,否则返回 nil。
	//当ctx被Cancel时,返回的chan会同时被 close 掉,也就变成“有信号”状态; 
	Done() <-chan struct{}
	
	//如果 ctx 已经被 canceled,Err 会返回执行 Cancel 时指定的error,否则返回nil;
	//也就是返回 Context 被取消的原因
	Err() error
	
	//Value 用来从 ctx 中根据指定的 key 提取对应的 value 
	Value(key interface{}) interface{}    

Context 提供了四种 context,分别是普通 context, 可取消的 context, 超时 context 以及带值的 context
总结为一个接口,四种实现,六个函数

BackGround() 是所有 Context 的 root,不能被 cancel

	// 普通context,通常这样调用: ctx, cancel := context.WithCancel(context.Background())
	func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
	 
	// 带超时的context,超时之后会自动close对象的Done,与调用CancelFunc的效果一样
	// WithDeadline 明确地设置一个指定的系统时钟时间,如果超过就触发超时
	// WithTimeout 设置一个相对的超时时间,也就是deadline设为timeout加上当前的系统时间
	// 因为两者事实上都依赖于系统时钟,所以可能存在微小的误差,所以官方不推荐把超时间隔设置得太小
	// 通常这样调用:ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
	func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
	 
	// 带有值的context,没有CancelFunc,所以它只用于值的多goroutine传递和共享
	// 通常这样调用:ctx := context.WithValue(context.Background(), "key", myValue)
	func WithValue(parent Context, key, val interface{}) Context

场景一:普通 Context(用的最多)
使用 Context 终止多个 Goroutine

func worker(ctx context.Context, name string) {
    go func() {
        for {
            select {
            case <-ctx.Done():
                fmt.Println(name, "got the stop channel")
                return
            default:
                fmt.Println("still working")
                time.Sleep(time.Second)
            }
        }
    }()
}

func main(){
    //context.Background() 返回一个空的 Context, 这个空的 Context 一般用于
    //整个 Context 树根节点。然后使用这个 context.WithCancel(parent) 函数
    //创建一个可取消的子 Context,再当作参数传递给 goroutine 使用,这样就可以使用这个
    //子 Context 跟踪这个 goroutine
    ctx, cancel := context.WithCancel(context.Background()) //返回根节点
    
    //开启多个 goroutine,传入 ctx
    go worker(ctx, "node01")
    go worker(ctx, "node02")
    go worker(ctx, "node03")
    
    time.Sleep(2 * time.Second)
    fmt.Println("stop the goroutine")
    
    //停止掉所有的 goroutine,包括根节点及其下面的所有子节点
    cancel()
    
    time.Sleep(2 * time.Second)
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/994004.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存