go之Context(一)

目录

    • Go语言中的Context
    • 基本用法
    • Context 的基础知识
    • 使用 Context 控制 goroutine
      • 使用 WithCancel() 取消goroutine操作
      • 使用WithTimeout()设置goroutine超时操作
      • 使用 WithDeadline() 设置goroutine截止时间
      • 使用WithValue()向goroutine传递值
    • 总结

Go语言中的Context

Context 是 Go 语言中非常重要的一个功能,它可以有效地传递请求的截止时间,以及请求的上下文(请求中包含的元数据)等信息。Context 是 Go 语言中超时设置、取消信号传递、goroutine 间数据传递等功能的重要组成部分。

在下面的文章中,我们将了解如何在 Go 语言中使用 Context
基本用法
在 Go 语言中,Context 被定义为一个接口类型,它包含了三个方法:

基本用法

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key any) any
}
  1. Deadline() 方法用于获取 Context 的截止时间,
  2. Done() 方法用于返回一个只读的 channel,用于通知当前 Context 是已否经被取消
  3. Err() 方法用于获取 Context 取消的原因,
  4. Value() 方法用于获取 Context 中保存的键值对数据。

Context 的基础知识

Context 表示一个运行环境中的请求,它是传递请求中的元数据(例如截止时间、取消信号、请求传递的值等)的机制。Context 中包含一个 Done 通道,可以用来在请求被取消或者超时时通知相关的 goroutine 停止处理请求工作。

在初始化 Context 时,我们可以使用 context.Background() 来创建一个根 Context,然后使用 WithCancelWithDeadlineWithTimeout 或者 WithValue 等函数来创建派生的 Context

这里有一些关于 Context 的一般使用规则:

  1. 不要将 Context 保存在结构体中,而是应该将其作为函数参数传递。
  2. Context 应该优先使用在请求的级别上,如在函数之间传递它,而不是在其他类型的代码之间传递它。
  3. 不要向不知情的代码中传递 nilContext,应该使用 context.TODO() 表示不确定的 Context

使用 Context 控制 goroutine

使用 WithContext 函数,可以将传递的 Context 对象传递给 goroutine 的启动函数,从而把 Context 对象传递到新启动的 goroutine 中。

使用 WithCancel() 取消goroutine操作

例如:

func myFunc(ctx context.Context) error {
    select {
        case <-time.After(time.Second):
            fmt.Println("请求完成")
        case <-ctx.Done():
            fmt.Println("请求取消")
            return ctx.Err()
    }
}
 
func main(){
    ctx, cancel := context.WithCancel(context.Background())
    go myFunc(ctx)
    time.Sleep(time.Millisecond*1500)
    cancel()
    time.Sleep(time.Second)
}

main 函数中,创建了根 Context,然后使用 WithContextContext 对象传递到 myFunc 中。在这个例子中,myFunc 会等待 1 秒钟来完成请求,然后会检查 Context 是否已经取消。如果 Context 取消,程序将立即返回错误信息。

main 函数中,等待 1.5 秒,然后调用 cancel 函数来取消 Context 中的 goroutine。goroutine 检测到受到取消操作后,打印出请求取消的消息,并返回 context.Canceled 错误信息。

在此例子中,我们使用了 context.Background() 函数创建了根的 Context。它是所有 Context 对象的默认值,没有任何附加值。 WithCancel 函数返回一个新的 Context 对象和一个 cancel 函数,这个函数可以用来取消 Context 中运行的 goroutine。

使用WithTimeout()设置goroutine超时操作

使用 context.WithTimeout 可以创建在指定的 timeout 期限内取消 Context 的示例。例如,如果请求处理时间超过 2 秒钟,则会发生超时操作:

func myFunc(ctx context.Context) error {
    select {
    case <-time.After(time.Second):
        fmt.Println("请求完成")
    case <-ctx.Done():
        fmt.Println("请求取消")
        return ctx.Err()
    }
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
    defer cancel()
    go myFunc(ctx)
    time.Sleep(time.Millisecond * 1500)
    fmt.Println("在规定时间内完成请求")
}

在这个例子中,我们使用 context.WithTimeout 函数在根 Context 上创建一个新的 Context,并设置请求时间超时为 2 秒钟。在 main 函数中:

启动了一个 myFunc goroutine,并等待 1.5 秒钟。然后输出一个完成请求的消息,因为 myFunc 在 1 秒钟内完成了这个请求。这说明,尽管使用了超时的 Context,但是在请求完成之前已经完成,并没有进行超时处理。

使用 WithDeadline() 设置goroutine截止时间

在这个示例中,我们使用了 context.WithDeadline 函数来设置请求的截止时间:

func myFunc(ctx context.Context) error {
    select {
        case <-time.After(time.Second):
            fmt.Println("请求完成")
        case <-ctx.Done():
            fmt.Println("请求取消")
            return ctx.Err()
    }
}
 
func main(){
    d := time.Now().Add(time.Second * 2)
    ctx, cancel := context.WithDeadline(context.Background(), d)
    defer cancel()
    go myFunc(ctx)
    time.Sleep(time.Millisecond*1500)
    fmt.Println("在规定时间内完成请求")
    time.Sleep(time.Second)
}

在这个例子中,我们使用了 time.Now().Add 函数设置了 2 秒钟的截止时间,并使用 context.WithDeadline 函数来创建一个包含截止时间的新 Context。当超过这个截止时间,将会发生一个取消操作。

main 函数中,等待 1.5 秒钟并输出完成请求的消息,最后等待 1 秒钟,是为了让 myFunc 在截止时间到来时有足够的时间来处理并取消请求。如果 myFunc 在截止时间到来之前完成请求,程序就会正常结束。

使用WithValue()向goroutine传递值

Context 还可以用来传递值,它可以传递键值对形式的元数据。传递的值必须是线程安全的类型,例如 string 或者 int 等基本数据类型,或者其他可以被并发访问的结构体。

下面的示例中,我们将会使用 WithValue 函数传递整数值:

func myFunc(ctx context.Context) {
    v := ctx.Value("key")
    fmt.Println("值:", v)
}
 
func main(){
    ctx := context.WithValue(context.Background(), "key", 100)
    myFunc(ctx)
}

在这个例子中,我们使用 WithValue 函数传递一个整型值 100,在 myFunc 中,使用键 key 将值传递给 goroutine。由于使用了 WithValue 函数,该值可以通过 ctx.Value 来访问。

需要注意的是,context.Value 函数的性能较差,在传递大量数据时,应该使用其他方法。

总结

在本文中,我们介绍了 Go 语言中的 Context,它是一个重要的功能,可以用来传递请求截止时间、取消信号、元数据等信息。我们还介绍了 WithContextWithTimeoutWithDeadlineWithValue 函数,并展示了如何在不同场景下使用 Context

通过了解 Context 的基础知识和使用方法,我们可以更好地控制 goroutine,提高程序的可靠性和性能。

你可能感兴趣的:(golang)