11

Golang 语言标准库 context 包控制 goroutine

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzA4Mjc1NTMyOQ%3D%3D&%3Bmid=2247484231&%3Bidx=1&%3Bsn=c807470c4a8368b9ce5bf3b2f6caab7b
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

01

介绍

在 Go1.7 中,标准库加入了 context 包,context 包定义了一个 Context (上下文)类型,可以在 Api 之间和进程之间传递信息,还提供了超时(timeout)和取消(cancel)机制。

Go 标准库中,database/sql,net,net/http 等包中都使用了 Context。

在 Go 应用开发中,一般用于请求链路中传递上下文信息,控制子 goroutine 等场景中。

02

Context 接口

type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}

context 包定义了一个 Context 接口,包含 4 个方法:

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

    Deadline 方法返回结果有两个,第一个是截止时间,到了这个截止时间,Context 会自动取消;第二个是一个 bool 类型的值,如果 Context 没有设置截止时间,第二个返回结果是 false,如果需要取消这个 Context,就需要调用取消函数。

  • Done() <-chan struct{}

    Done 方法返回一个只读的 channel 对象,类型是 struct{},在 goroutine 中,如果 Done 方法返回的结果可以被读取,代表父 Context 调用了取消函数。

  • Err() error

    Err 方法返回 Context 被取消的原因。

  • Value(key interface{}) interface{}

    Value 方法返回此 Context 绑定的值。它是一个 kv 键值对,通过 key 获取对应 value 的值。

03

创建 Context

context 包中包含两个生成顶层 Context 的方法:

  • func Background() Context

    Background 方法一般用于 main 函数,init 函数,测试和创建根 Context 的时候。

  • func TODO() Context

    TODO 方法 ,当不清楚使用 哪个上下文 ,可以使用 TODO 方法。

这两个方法都是返回一个非 nil,空 Context,没有任何值,不会被 cancel,不会超时,没有截止日期。实际上,这两个方法的底层实现是一样的。

var (
background = new(emptyCtx)
todo = new(emptyCtx)
)


func Background() Context {
return background
}


func TODO() Context {
return todo
}

这两个方法都是 emptyCtx 的指针类型,是一个不可取消,没有设置截止时间,没有任何值得 Context。

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"
}

04

CancelFunc 类型

type CancelFunc func()

CancelFunc 用于主动让 goroutine 停止。多个goroutine 可以同时调用 CancelFunc。在第一个调用之后,随后对 CancelFunc 的调用什么也不做。

05

函数

创建了顶层 Context,想要创建子 Context,可以使用以下方法:

  • WithCancel

    WithCancel 方法,基于父 Context,接收一个父 Context 参数,生成一个新的子 Context,和一个 cancel 函数,用于取消 Context。

  • WithDeadline

    WithDeadline 方法,基于父 Context,接收一个父 Context 参数,和一个截止时间的参数,生成一个新的子 Context,和一个 cancel 函数,可以使用 cancel 函数取消 Context,也可以等到截止时间,自动取消 Context。

  • WithTimeout

    WithTimeout 方法,基于父 Context,接收一个父 Context 参数,和一个超时时间的参数,生成一个新的子 Context,和一个 cancel 函数,可以使用 cancel 函数取消 Context,也可以等到超时时间,自动取消 Context。

  • WithValue

    WithValue 方法,基于父 Context,生成一个新的子 Context,携带了一个 kv 键值对,一般用于传递上下文信息。

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context

示例代码:

var name string


func main () {
// ctx, cancle := context.WithCancel(context.Background())
// d := time.Now().Add(time.Second * 2)
// ctx, cancle := context.WithDeadline(context.Background(), d)
ctx, cancle := context.WithTimeout(context.Background(), time.Second * 2)
valueCtx := context.WithValue(ctx, name, "lucy")
go work(valueCtx)
time.Sleep(time.Second * 3)
fmt.Println("停止工作。")
cancle()
time.Sleep(time.Second * 5)
}


func work(ctx context.Context) {
for {
select {
case <- ctx.Done():
fmt.Println(ctx.Value(name), "工作结束!")
return
default:
fmt.Println(ctx.Value(name), "工作中。")
time.Sleep(time.Second * 1)
}
}
}

06

规范

  • 不要将上下文存储在结构类型中;而是将上下文以参数形式传递给需要它的函数。并且 Context 应该是第一个参数,该参数通常命名为 ctx。

  • 即使函数允许,也不要传递nil Context 的参数。如果不确定使用哪个上下文,就使用 context.TODO。

  • 可以将相同的 Context 上下文传递给在不同 goroutine 中运行的函数。上下文是线程安全的,可由多个 goroutine 同时使用。

07

总结

Context 一般用于控制 goroutine,使用 Context 主动取消 goroutine 的运行。除此之外,关于控制 goroutine,我们还可以使用 WaitGroup 和 channel 被动取消 goroutine。

B3yUfqm.jpg!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK