七叶笔记 » golang编程 » Golang Context 上下文 总结

Golang Context 上下文 总结

每个Goroutine在执行之前,都要先知道程序当前的执行状态,通常将这些执行状态封装在一个Context变量中,传递给要执行的Goroutine中。上下文则几乎已经成为传递与请求同生存周期变量的标准方法。在网络编程下,当接收到一个网络请求 Request ,处理Request时,我们可能需要开启不同的Goroutine来获取数据与逻辑处理,即一个请求Request,会在多个Goroutine中处理。而这些Goroutine可能需要共享Request的一些信息;同时当Request被取消或者超时的时候,所有从这个Request创建的所有Goroutine也应该被结束。

定义:

type Context interface {

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

Done() <-chan struct{}

Err() error

Value(key interface{}) interface{}

}

Deadline会返回一个超时时间.

Done方法返回一个信道(channel),当Context被撤销或过期时,该信道是关闭的,即它是一个表示Context是否已关闭的信号。

当Done信道关闭后,Err方法表明Context被撤的原因。

Value可以让Goroutine共享一些数据,当然获得数据是协程安全的。

核心方法

Context结构也应该像一棵树,叶子节点须总是由根节点衍生出来的。

要创建Context树,第一步就是要得到根节点,context.Background函数的返回值就是根节点:

func Background() Context

该函数返回空的Context,该Context一般由接收请求的第一个Goroutine创建,是与进入请求对应的Context根节点,它不能被取消、没有值、也没有过期时间。它常常作为处理Request的顶层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, Cancel Func)

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

示例:context.WithCancel

WithCancel返回一个继承的Context,这个Context在父Context的Done被关闭时关闭自己的Done通道,或者在自己被Cancel的时候关闭自己的Done。

WithCancel同时还返回一个取消函数cancel,这个cancel用于取消当前的Context。

package main

import (

“context”

log

“os”

“time”

)

var logg *log.Logger

func someHandler() {

ctx, cancel := context.WithCancel(context.Background())

go doStuff(ctx)

//10秒后取消doStuff

time.Sleep(10 * time.Second)

cancel()

}

//每1秒work一下,同时会判断ctx是否被取消了,如果是就退出

func doStuff(ctx context.Context) {

for { time.Sleep(1 * time.Second)

select {

case <-ctx.Done():

logg.Printf(“done”)

return

default:

logg.Printf(“work”)

}

}

}

func main() {

logg = log.New(os.Stdout, “”, log.Ltime)

someHandler()

logg.Printf(“down”)

}

示例:context.WithValue

func NewContext(ctx context.Context, userIP net.IP) context.Context {

return context.WithValue(ctx, userIPKey, userIP)

}

func FromContext(ctx context.Context) (net.IP, bool ) {

userIP, ok := ctx.Value(userIPKey).(net.IP)

return userIP, ok

}

总结:

context包通过构建树型关系的Context,来达到上一层Goroutine能对传递给下一层Goroutine的控制。对于处理一个Request请求操作,需要采用context来层层控制Goroutine,以及传递一些变量来共享。

Context对象的生存周期一般仅为一个请求的处理周期。即针对一个请求创建一个Context变量(它为Context树结构的根);在请求处理结束后,撤销此ctx变量,释放资源。

每次创建一个Goroutine,要么将原有的Context传递给Goroutine,要么创建一个子Context并传递给Goroutine。

Context能灵活地存储不同类型、不同数目的值,并且使多个Goroutine安全地读写其中的值。

当通过父Context对象创建子Context对象时,可同时获得子Context的一个撤销函数,这样父Context对象的创建环境就获得了对子Context将要被传递到的Goroutine的撤销权。

更多内容请关注每日编程,每天进步一点。

相关文章