golang中context的使用

本文深入解析Go语言中的Context包,介绍其核心功能包括父协程控制所有子协程、协程间共享数据以及超时取消协程的处理。通过具体示例展示了如何使用WithCancel、WithDeadline等函数创建和管理协程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

context 包的官方操作

https://golang.org/pkg/context/

不想看前面的关于原理的啰嗦,可以直接看后面的例子,真是一目了然啊。回过来看原理会更加清晰

Variables
type CancelFunc
type Context
    func Background() Context
    func TODO() Context
    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


基本结构

    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共享一些数据,当然获得数据是协程安全的。

父节点

func Background() 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 interface{}, val interface{}) Context
调用CancelFunc对象将撤销对应的Context对象,这样父结点的所在的环境中,获得了撤销子节点context的权利,当触发某些条件时,可以调用CancelFunc对象来终止子结点树的所有routine。在子节点的routine中,需要用类似下面的代码来判断何时退出routine:

select {
    case <-cxt.Done():
        // do some cleaning and return
}

使用上下文存储数据


type valueCtx struct {
    Context
    key, val interface{}
}

func WithValue(parent Context, key, val interface{}) Context {
    if key == nil {
        panic("nil key")
    }
    ......
    return &valueCtx{parent, key, val}
}

func (c *valueCtx) Value(key interface{}) interface{} {
    if c.key == key {
        return c.val
    }
    return c.Context.Value(key)
}

context上下文数据的存储就像一个树,每个结点只存储一个key/value对。WithValue()保存一个key/value对,它将父context嵌入到新的子context,并在节点中保存了key/value数据。Value()查询key对应的value数据,会从当前context中查询,如果查不到,会递归查询父context中的数据。

值得注意的是,context中的上下文数据并不是全局的,它只查询本节点及父节点们的数据,不能查询兄弟节点的数据。

手动cancel和超时cancel

type cancelCtx struct {
    Context      // 保存parent Context
    done chan struct{}
    mu       sync.Mutex
    children map[canceler]struct{}
    err      error
}

// A canceler is a context type that can be canceled directly. The
// implementations are *cancelCtx and *timerCtx.
type canceler interface {
    cancel(removeFromParent bool, err error)
    Done() <-chan struct{}
}

cancelCtx结构体中children保存它的所有子canceler, 当外部触发cancel时,会调用children中的所有cancel()来终止所有的cancelCtx。done用来标识是否已被cancel。当外部触发cancel、或者父Context的channel关闭时,此done也会关闭。

超时的处理

timerCtx结构体中deadline保存了超时的时间,当超过这个时间,会触发cancel。

type timerCtx struct {
    cancelCtx     //cancelCtx.Done()关闭的时机:1)用户调用cancel 2)deadline到了 3)父Context的done关闭了
    timer    *time.Timer
    deadline time.Time
}

func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
    ......
    c := &timerCtx{
        cancelCtx: newCancelCtx(parent),
        deadline:  deadline,
    }
    propagateCancel(parent, c)
    d := time.Until(deadline)
    if d <= 0 {
        c.cancel(true, DeadlineExceeded) // deadline has already passed
        return c, func() { c.cancel(true, Canceled) }
    }
    c.mu.Lock()
    defer c.mu.Unlock()
    if c.err == nil {
        c.timer = time.AfterFunc(d, func() {
            c.cancel(true, DeadlineExceeded)
        })
    }
    return c, func() { c.cancel(true, Canceled) }
}

总的来说,context提供了如下几种功能

1 父协程控制所有子协程
2 协程间共享数据
3 超时取消协程的处理

来个代码说明下

package main

import (
    "context"
    "log"
    "os"
    "time"
)

const (
    GOLABLE_KEY = "test123"
)

func main() {
    //父context控制子context
    controlAllConrrent()
}

func controlAllConrrent() {

    logg := log.New(os.Stdout, "", log.Ltime)
    handleSome()
    logg.Println("over ")
}

//父协程
func handleSome() {
    //ctx, cancelFunc := context.WithCancel(context.Background())
    //ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*2)
    ctx, cancelFunc := context.WithDeadline(context.Background(), time.Now().Add(time.Second*2))

    ctx = context.WithValue(ctx, GOLABLE_KEY, "1234")

    go workerFunc(ctx, "str1")
    go workerFunc(ctx, "str2")
    time.Sleep(time.Second * 3)
    cancelFunc()
}

//子协程
func workerFunc(ctx context.Context, showStr string) {
    for {
        time.Sleep(time.Second * 1)
        select {
        case <-ctx.Done():
            log.Println("done")
            return
        default:
            val123 := ctx.Value(GOLABLE_KEY).(string)
            log.Println(val123, showStr)
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值