go context包分析

context在go中被称作上下文,主要是用来控制go中生成的goroutine,当goroutine少的时候还好控制,当goroutine一生百,百生千,这时候该怎么管理呢,于是context就在这里派上了用场。

func main() {
    stop := make(chan bool)

    go func() {
        for {
            select {
            case <-stop:
                fmt.Println("监控退出,停止了...")
                return
            default:
                fmt.Println("goroutine监控中...")
                time.Sleep(2 * time.Second)
            }
        }
    }()

    time.Sleep(10 * time.Second)
    fmt.Println("可以了,通知监控停止")
    stop<- true
    //为了检测监控过是否停止,如果没有监控输出,就表示停止了
    time.Sleep(5 * time.Second)

}

上面的例子是用channel来控制goroutine,但是如果goroutine再生成新的goroutine,我们就要建更多channel,代码量也会更多,内存消耗也比较大,这时候就体现了context的优势了,下面看下context的例子。

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    go func(ctx context.Context) {
        for {
            select {
            case <-ctx.Done():
                fmt.Println("监控退出,停止了...")
                return
            default:
                fmt.Println("goroutine监控中...")
                time.Sleep(2 * time.Second)
            }
        }
    }(ctx)

    time.Sleep(10 * time.Second)
    fmt.Println("可以了,通知监控停止")
    cancel()
    //为了检测监控过是否停止,如果没有监控输出,就表示停止了
    time.Sleep(5 * time.Second)
}

主要就是用channel换成context,似乎看起来跟使用channel差不多嘛,那是因为举的例子只用了一个goroutine,如果是多个比如几千,几万个呢,goroutine再生成goroutine,这样我们光是在channel的判断上就要耗费很多时间。我们先简单看下上面例子的代码
1、context.Background(),这个作用就是相当于注册一个context根节点,以后我们通过context.WithCancel获得的context都是注册在这根节点上面的。
2、context.WithCancel 这个就是获得一个子Context,同时得到cancel方法,这个在后面用到
3、 将第二步得到的Context作为上下文传递到goroutine里,同时在goroutine里监听context.Done() 的信号,这个Done信号是什么时候传递的呢,看第四步
4、第二步我们通过WithCancel得到的cancel方法,在主函数中调用,这个时候,第三步中context.Done()就得到了信号触发。于是goroutine退出,从而实现了控制goroutine。
整体流程就是这样,方法也比较简单。下面我们看下context接口实现

type Context interface {
    Deadline() (deadline time.Time, ok bool)

    Done() <-chan struct{}

    Err() error

    Value(key interface{}) interface{}
}

这个接口共有4个方法,了解这些方法的意思非常重要,这样我们才可以更好的使用他们。

Deadline方法是获取设置的截止时间的意思,第一个返回式是截止时间,到了这个时间点,Context会自动发起取消请求;第二个返回值ok==false时表示没有设置截止时间,如果需要取消的话,需要调用取消函数进行取消。

Done方法返回一个只读的chan,类型为struct{},我们在goroutine中,如果该方法返回的chan可以读取,则意味着parent context已经发起了取消请求,我们通过Done方法收到这个信号后,就应该做清理操作,然后退出goroutine,释放资源。

Err方法返回取消的错误原因,因为什么Context被取消。

Value方法获取该Context上绑定的值,是一个键值对,所以要通过一个Key才可以获取对应的值,这个值一般是线程安全的。可以通过这个方法进行上下文之间的传值操作

上面都是接口指定方法,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)
func WithValue(parent Context, key, val interface{}) Context

除了第一个已经在示例中用到了,其他几个都是依据context接口里面的方法实现,用法可自行去了解。

context包分析完了,接下来对于net包来进行分析,net包比较大,需要用多个篇幅来介绍。

你可能感兴趣的:(go context包分析)