Golang—context

570 阅读4分钟

作用

1、web编程中,一个请求对应多个goroutine之间的数据交互
2、超时控制,控制异步请求超时
3、上下文控制,跟踪请求的流程

结构

1、底层结构

// 请求上下文信息
context

// 作用:用来简化对于处理单个请求的多个gouroutine之间与请求域的数据,取消信号,截止时间等相关操作
type Context interface { 
    // 设置的截止时间
    // deadline : 到了该时间点,context会自动发起取消请求
    // ok == false 表示 没有设置截止时间,如果需要取消的话,需要调用取消函数
    Deadline() (deadline time.Time, ok bool) 

    // 返回一个只读的chan,类型为struct{},
    // 在goroutine中,如果该方法返回的chan可以读取,则意味着parent context已经发起了取消请求,通过Done()方法收到这个信号后,应该做清理操作,
    // 然后退出goroutine,释放资源,之后Err()方法会返回一个错误,告知为什么Context被取消
    Done() <-chan struct{} 
    
    //返回取消的错误原因,因为什么Context被取消
    Err() error 

    //获取Context上绑定的值,是一个健值对,通过key才可以获取对应的值
    Value(key interface{}) interface{} 
}

Context的继承
// 传递一个父Context为参数,返回子Context及一个取消函数来取消Context
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

// 与WithCannel类似,会多传递一个截止时间参数,意味着到了这个时间点,会自动取消Context,当前我们也可以不等到这个时候,提前调用取消函数进行取消
func WithDeadline(parent Context, deadline time.Time)(Context,CancelFunc)

// 与WithDeadline基本一样,提供一个时间长度,多长时间后自动取消
func WithTimeout(parent Context,timeout time.Duration)(Context,CancelFunc)

// 是为了生成一个绑定了一个健值对数据的Context,绑定的数据可以通过Context.Value方法访问
func WithValue(parent Context, key ,val interface{}) Context

2、空的结构

// An emptyCtx is never canceled, has no values, and has no deadline. It is not
// struct{}, since vars of this type must have distinct addresses.
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
}

func (e *emptyCtx) String() string {
	switch e {
	case background:
		return "context.Background"
	case todo:
		return "context.TODO"
	}
	return "unknown empty Context"
}

3、传值结构

// A valueCtx carries a key-value pair. It implements Value for that key and
// delegates all other calls to the embedded Context.
type valueCtx struct {
	Context
	key, val interface{}
}

4、取消结构

// A cancelCtx can be canceled. When canceled, it also cancels any children
// that implement canceler.
type cancelCtx struct {
	Context

	mu       sync.Mutex            // protects following fields
	done     chan struct{}         // created lazily, closed by first cancel call
	children map[canceler]struct{} // set to nil by the first cancel call
	err      error                 // set to non-nil by the first cancel call
}

5、超时结构

// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
// implement Done and Err. It implements cancel by stopping its timer then
// delegating to cancelCtx.cancel.
type timerCtx struct {
	cancelCtx
	timer *time.Timer // Under cancelCtx.mu.

	deadline time.Time
}

案例

1、传值

// with_value
// 传值
func with_value() {
	// 声明一个根上下文信息
	ctx, cancel := context.WithCancel(context.Background())
	valueCtx := context.WithValue(ctx, "key", "add value")

	go watch(valueCtx)
	time.Sleep(5 * time.Second)
	cancel()
	time.Sleep(1 * time.Second)
}

func watch(ctx context.Context) {
	fmt.Println("go watch")
	for {
		select {
		case <-ctx.Done():
			// 上下文被取消
			fmt.Println(ctx.Value("key"), "is cancel")
			time.Sleep(1 * time.Second)
		default:
			fmt.Println(ctx.Value("key"), "int goroutine")
			time.Sleep(1 * time.Second)
		}
	}
}

2、设置截止时间

// with_deadtime
// 截止时间
func with_deadtime() {
	dt := time.Now().Add(3 * time.Second)
	ctx, cancel := context.WithDeadline(context.Background(), dt)
	defer cancel()

	go func(ctx context.Context) {
		for {
			select {
			case <-time.After(2 * time.Second):
				fmt.Println("到达截止时间")
			case <-ctx.Done():
				fmt.Println("上下文取消")
				return
			}
		}
	}(ctx)

	time.Sleep(5 * time.Second)
}

3、设置超时


// with_timeout
// 超时
func with_timeout() {
	ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)
	defer cancel()

	fmt.Println("with_timeout start")
	var wg sync.WaitGroup
	wg.Add(1)
	go work(ctx, &wg)
	wg.Wait()
	fmt.Println("with_timeout end")
}

func work(ctx context.Context, wg *sync.WaitGroup) error {
	defer wg.Done()

	for i := 0; i < 1000; i++ {
		select {
		case <-time.After(1 * time.Second):
			fmt.Println("指定时间", i)
		case <-ctx.Done():
			fmt.Println("Cancel context", i)
			return ctx.Err()
		}
	}
	return nil
}

4、并发请求一个超时取消所有的请求


func task1() int {
	time.Sleep(3 * time.Second)
	return 1
}

func task2() int {
	time.Sleep(2 * time.Second)
	return 2
}

func task3() int {
	time.Sleep(2 * time.Second)
	return 3
}

// with_url
// 一个请求触发三个任务 并发完成,出现一个超时则全部取消
// 1、要求处理三个结果之和
// 2、返回时间不超过指定时间
func with_url() {
	var res, sum int
	// 请求标志
	sucess := make(chan int)
	// 保存每个任务的结果
	resChan := make(chan int, 3)
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	// 使用sync保证任务全部完成之后推出
	var wg sync.WaitGroup
	wg.Add(3)

	go func() {
		resChan <- task1()
		wg.Done()
	}()
	go func() {
		resChan <- task2()
		wg.Done()
	}()
	go func() {
		resChan <- task3()
		wg.Done()
	}()

	go func() {
		for {
			select {
			case res = <-resChan:
				sum += res
				fmt.Println("add", res)
			case <-sucess:
				fmt.Println("所有任务完成之后的结果", sum)
				// wg.Done()
				return
			case <-ctx.Done():
				fmt.Println("出现超时后的结果", sum)
				// wg.Done()
				return
			}
		}
	}()

	wg.Wait()
	// 表示任务已经完成
	sucess <- 1
}

func main() {
	with_url()
}

参考