zoukankan      html  css  js  c++  java
  • Go的Context用法

    Type Context

    done Channel 机制类似实现取消机制, 一层封装,

    解决问题

    1. 类似于done Channel, 取消goroutinue
    2. 传递取消的原因: 超时或者任务完成
    3. 优雅地组成取消机制的调度树(所有Ctx只能向下触发取消机制)
    type Context interface {
    	// ok表示是否有设置deadline
    	DeadLine() (deadline time.Time, ok bool) 
    
    	Done() <-chan struct{}
    
    	Err() error
    
    	Value(key interface{}) interface{}
    }
    
    • Go 开发人员, 认为这种取消机制只要是用在请求方案场景(request-scoped), 所以拓展了Value 方法
    • 不提供取消api, 避免接收者越界进行取消动作

    两种机制

    • 提供取消机制, 和取消原因
    • 提供数据包传递机制

    使用惯例

    • 默认放在传入参数第一个

    取消机制

    因为context 是一个接口, 所以传递过程是无有效方法改变内容的, 所以提供以下api进行修改.
    当前使用者可以通过包裹父级Context实现, 自己定制的Context给下层使用.
    各层都可以实现自己对子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)
    

    顶层实例Ctx方法

    	// 返回一个空的Context
    	func Background() Context
    
    	// 代码开发过程中的一个占位符, 也是返回空的Context. 不应该出现在生产代码里
    	// 千万不要传nil, 不知道传什么就放Todo()
    	func TODO() Context
    

    Example:

    package main
    
    import (
    	"context"
    	"fmt"
    	"sync"
    	"time"
    )
    
    func main() {
    	UseContextCancel(5 * time.Second)
    	UseContextCancel(10 * time.Second)
    	// Output:
    	// run away success: context deadline exceeded
    	// context canceled
    	// hello, Ian
    	// bye
    }
    
    func UseContextCancel(runInTime time.Duration) {
    
    	wg := sync.WaitGroup{}
    	ctx, cancel := context.WithCancel(context.Background())
    
    	wg.Add(1)
    	go func() {
    		defer wg.Done()
    
    		if err := runAwayOrHi(ctx, "Ian", runInTime); err != nil {
    			fmt.Println(fmt.Sprintf("run away success: %s", err))
    			cancel()
    		}
    	}()
    
    	wg.Wait()
    
    	wg.Add(1)
    	go func() {
    		defer wg.Done()
    
    		if err := sayBye(ctx); err != nil {
    			fmt.Println(err)
    		}
    	}()
    	wg.Wait()
    }
    
    func runAwayOrHi(ctx context.Context, who string,
    	runInTime time.Duration) error {
    
    	// run away in time, so avoid to say hi QAQ!
    	ctx, cancel := context.WithTimeout(ctx, runInTime)
    	defer cancel()
    
    	hi, err := sayHi(ctx, who)
    	if err != nil {
    		return err
    	}
    
    	fmt.Println(hi)
    	return nil
    }
    
    func sayHi(ctx context.Context, who string) (words string, err error) {
    
    	shyTime := 7 * time.Second
    	// use deadline, avoid wasting time to wait
    	if deadline, ok := ctx.Deadline(); ok {
    		if deadline.Sub(time.Now().Add(shyTime)) < 0 {
    			return "", context.DeadlineExceeded
    		}
    	}
    
    	shying := time.NewTimer(shyTime)
    	select {
    	case <-ctx.Done():
    		return "", ctx.Err()
    	case <-shying.C:
    		return fmt.Sprintf("hello, %s", who), nil
    	}
    }
    
    func sayBye(ctx context.Context) error {
    
    	select {
    	case <-ctx.Done():
    		return ctx.Err()
    	default:
    		fmt.Println("bye")
    		return nil
    	}
    }
    

    传递数据

    在context里面实现, 这种非类型安全的key-value是很有争议性的.
    一个是非类型安全, 主要还是为什么需要这个数据包功能.

    Go官方给的理由

    Use context values only for request-scoped data that transits processes
    and API boundaries, not for passing optional parameters to functions.

    糙翻译: context 的value是用在请求(eg: http)的数据传递.

    使用方法

    	func WithValue(parent Context, key, val interface{}) Context
    

    注意事项

    • 传递的值是不可变的
    • 可以定义全局Key-type和Key, 或者专门的package定义Key, 再提供API. 实现类型安全
    • 尽量使用简单的类型(string, int...)

    常见的Request数据

    • Request ID
    • User ID
    • URL
    • API
    • Token

    内容参考

    Concurrency in Go

  • 相关阅读:
    面试笔试
    scala(9) Monad
    scala (8) 模糊匹配
    scala (7) Set and Tuple
    scala (6) Map
    scala (5) 可变序列和不可变序列
    scala (4) 可变数组和不可变数组
    scala (3) Function 和 Method
    scala (2) while 和变量
    scala (1) for 循环
  • 原文地址:https://www.cnblogs.com/Gilfoyle/p/15214754.html
Copyright © 2011-2022 走看看