golang学习之--context实现原理【转载】

概述

    context是Go中广泛使用的程序包,由Google官方开发,在1.7版本引入。它用来简化在多个go routine传递上下文数据、(手动/超时)中止routine树等操作,比如,官方http包使用context传递请求的上下文数据,gRpc使用context来终止某个请求产生的routine树。每个Context应该视为只读的,通过WithCancel、WithDeadline、WithTimeout和WithValue函数可以基于现有的一个Context(称为父Context)派生出一个新的Context(称为子Context)。其中WithCancel、WithDeadline和WithTimeout函数除了返回一个派生的Context以外,还会返回一个与之关联CancelFunc类型的函数,用于关闭Context。通过调用CancelFunc来关闭关联的Context时,基于该Context所派生的Context也都会被关闭,并且会将自己从父Context中移除,停止和它相关的timer。

核心接口

1

2

3

4

5

6

type Context interface {

    Deadline() (deadline time.Time, ok bool)

    Done() <-chan struct{}

    Err() error

    Value(key interface{}) interface{}

}

 根据如上接口显示对每个方法介绍

  • Done会返回一个channel,当该context被取消的时候,该channel会被关闭,同时对应的使用该context的routine也应该结束并返回。
  • Context中的方法是协程安全的,这也就代表了在父routine中创建的context,可以传递给任意数量的routine并让他们同时访问。
  • Deadline会返回一个超时时间,routine获得了超时时间后,可以对某些io操作设定超时时间。
  • Value可以让routine共享一些数据,当然获得数据是协程安全的。

在请求处理的过程中,会调用各层的函数,每层的函数会创建自己的routine,是一个routine树。所以,context也应该反映并实现成一棵树。

创建context

  在创建context时会通过context.Background函数的返回值是一个空的context,作为树的根结点。并且提供4个常用函数去创建其子节点。

1

2

3

4

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 interface{}, val interface{}) Context

 需要注意的是每个函数都会返回一个CancelFunc。调用CancelFunc对象将撤销对应的Context对象,这样父结点的所在的环境中,获得了撤销子节点context的权利,当触发某些条件时,可以调用CancelFunc对象来终止子结点树的所有routine。

我们在子集中可以通过 cxt.Done()是否为空来判断。

1

2

3

4

select {

    case <-cxt.Done():

        // do some cleaning and return

}

WithDeadlineWithTimeoutWithCancel多了一个时间参数,它指示context存活的最长时间。如果超过了过期时间,会自动撤销它的子context。所以context的生命期是由父context的routine和deadline共同决定的。

WithValue返回parent的一个副本,该副本保存了传入的key/value,而调用Context接口的Value(key)方法就可以得到val。注意在同一个context中设置key/value,若key相同,值会被覆盖。

示例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

package main

 

import (

    "fmt"

    "time"

    "golang.org/x/net/context"

)

 

func main() {

    ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*5)

    ctx = context.WithValue(ctx, "Test""123456")

    defer cancelFunc()

     

    if t, ok := ctx.Deadline(); ok {

        fmt.Println(time.Now())

        fmt.Println(t.String())

    }

    go func(ctx context.Context) {

        fmt.Println(ctx.Value("Test"))

        for {

            select {

            case <-ctx.Done():

                fmt.Println(ctx.Err())

                return

            default:

                continue

            }

        }

    }(ctx)

    time.Sleep(time.Second * 3)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值