golang中 Context的四大用法

1. Context定义

context 是上下文的意思,它最大的作用就是在上下文中传递信息,在golang当中是一种接口类型,凡是实现该接口的类都可称为是一种 context,context里面有四个方法,将这四个方法全部实现就代表实现了context的一个类,它与 waitGroup 最大的不同点是 context 对于派生 goroutine 有更强的控制力,它可以控制多级的goroutine

2. Context四大用法

1. 数据传输(大部分时候用的都是context的这个用法

Go 复制代码
type UserInfo struct {  
    Name string  
}  
  
func GetUser(ctx context.Context) {  
    // 从上下文提取数据并打印
    fmt.Println(ctx.Value("name").(UserInfo).Name)  
}  
  
func main() {  
    ctx := context.Background()  
    // 向上下文添加键值对数据
    ctx = context.WithValue(ctx, "name", UserInfo{  
       Name: "leyinlin",  
    })  
    GetUser(ctx)  
}

流程:

  1. 在上下文中添加键值对数据(WithValue方法);

  2. 从上下文中获取数据(Value方法);两个步骤,简单易懂

2. 协程取消

Go 复制代码
var wait = sync.WaitGroup{}  
  
func main() {  
    // 记录开始时间  
    t1 := time.Now()  
  
    // 返回上下文和取消函数两个值,当调用该取消函数时,所有监听该上下文的协程都会被通知取消  
    ctx, cancel := context.WithCancel(context.Background())  
  
    // 计数  
    wait.Add(1)  
    go func() {  
       ip, err := GetIp(ctx)  
       if err != nil {  
          fmt.Println(err)  
       }  
       fmt.Println(ip)  
  
    }()  
    go func() {  
       time.Sleep(2 * time.Second)  
       // 取消协程  
       cancel()  
  
    }()  
    wait.Wait()  
    fmt.Println("执行完成", time.Since(t1))  
}  
  
func GetIp(c context.Context) (ip string, err error) {  
    //启动一个协程监听上下文的取消信号,当上下文被取消时,c.Done()通过会关闭  
    go func() {  
       select {  
       case <-c.Done():  
          fmt.Println("协程取消", c.Err())  
          err = c.Err()  
          wait.Done()  
          return  
       }  
    }()  
    time.Sleep(4 * time.Second)  
    ip = "192.168.200.1"  
    wait.Done()  
    return ip, nil  
}

流程:

  1. 主协程启动两个子协程:一个用于获取 IP,另一个用于在 2 秒后触发取消。

  2. 获取 IP 的协程内部启动了一个监听协程,用于监听取消信号。

  3. 2 秒后,取消协程执行 `cancel()`,通知上下文被取消。

  4. 监听协程检测到上下文取消,设置错误并减少 `WaitGroup` 计数。

  5. 获取 IP 的协程在 4 秒后完成任务,设置 IP 并减少计数。

  6. 主协程在计数归零后继续执行,打印执行完成的时间。

3. 截止时间

Go 复制代码
func main() {  
    var wg = sync.WaitGroup{}  
  
    t1 := time.Now()  
  
    // 返回新的上下文和取消函数两个值,上下文会在当前时间加上2秒后自动取消  
    ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(2*time.Second))  
  
    wg.Add(1)  
    go func() {  
       ip, err := GetIp1(ctx, &wg)  
       if err != nil {  
          fmt.Println(err)  
       }  
       fmt.Println(ip)  
  
    }()  
    cancel()  
    wg.Wait()  
    fmt.Println("执行完成", time.Since(t1))  
}  
  
// GetIp1 协程 参数有带截止时间的上下文和 waitGroupfunc GetIp1(c context.Context, wg *sync.WaitGroup) (ip string, err error) {  
    go func() {  
       select {  
       // 当上下文被取消时,该通道会关闭,然后执行相应逻辑  
       case <-c.Done():  
          fmt.Println("协程取消", c.Err())  
          err = c.Err()  
          wg.Done()  
          return  
       }  
    }()  
    // 模拟获取ip耗时操作,这里休眠了4秒,但是截止时间是2秒,所以这个操作会超出截止时间,  
    // 导致上下文自动取消  
    time.Sleep(4 * time.Second)  
    ip = "192.168.200.1"  
    wg.Done()  
    return ip, nil  
}

流程:

  1. 通过 `context.WithDeadline` 设置一个截止时间,超过这个时间上下文会自动取消。

  2. `GetIp1` 函数中的任务需要在截止时间之前完成,否则上下文取消会触发取消机制。

  3. 任务协程内部启动了一个监听协程,用于监听上下文的取消信号,确保任务可以在截止时间到达时及时响应。

4. 超时时间

Go 复制代码
func main() {  
    var wg = sync.WaitGroup{}  
  
    t1 := time.Now()  
  
    // 返回上下文和取消函数两个值,上下文会在2秒后自动取消  
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)  
  
    wg.Add(1)  
    // 启动协程执行 GetIp2函数,传递带超时的上下文和waitGroup  
    go func() {  
       ip, err := GetIp2(ctx, &wg)  
       if err != nil {  
          fmt.Println(err)  
       }  
       fmt.Println(ip)  
  
    }()  
    //取消上下文  
    cancel()  
    wg.Wait()  
    fmt.Println("执行完成", time.Since(t1))  
}  
  
func GetIp2(c context.Context, wg *sync.WaitGroup) (ip string, err error) {  
    go func() {  
       select {  
       // 协程被取消,打印错误  
       case <-c.Done():  
          fmt.Println("协程取消", c.Err())  
          err = c.Err()  
          wg.Done()  
          return  
       }  
    }()  
    // 模拟任务时长  
    time.Sleep(4 * time.Second)  
    ip = "192.168.200.1"  
    wg.Done()  
    return ip, nil  
}

流程:

  1. 通过 `context.WithTimeout` 设置一个超时时间,上下文会在指定时间后自动取消。

  2. `GetIp2` 函数中的任务需要在超时时间之前完成,否则上下文取消会触发取消机制。

  3. 任务协程内部启动了一个监听协程,用于监听上下文的取消信号,确保任务可以在超时时间到达时及时响应。

3. 超时时间和截止时间的区别

**时间类型:1.**超时时间是相对时间(从调用时开始计算)

  1. 截止时间是绝对时间(指定的未来时间)

**使用场景:1.**超时时间任务需要在调用后一定时间内完成

  1. 截止时间任务需要在指定时间之前完成

**总结:**超时时间适合用于任务需要在调用后一定时间内完成的场景, 而截止时间适合用于任务需要在指定时间之前完成的场景。两者都用于控制任务的执行时间,但适用场景有所不同。

相关推荐
wasp5202 小时前
AgentScope深入分析-设计模式与架构决策分分析
开发语言·python·agent·agentscope
山土成旧客2 小时前
【Python学习打卡-Day26】函数的艺术(上):从基础定义到参数魔法
开发语言·python·学习
Coder_Boy_2 小时前
【人工智能应用技术】-基础实战-小程序应用(基于springAI+百度语音技术)智能语音控制-Java部分核心逻辑
java·开发语言·人工智能·单片机
MACKEI2 小时前
业务域名验证文件添加操作手册
java·开发语言
车载测试工程师2 小时前
CAPL学习-AVB交互层-媒体函数1-回调&基本函数
网络·学习·tcp/ip·媒体·capl·canoe
apihz2 小时前
货币汇率换算免费API接口(每日更新汇率)
android·java·开发语言
gf13211112 小时前
python_检测音频人声片段
开发语言·python·音视频
richxu202510012 小时前
嵌入式学习之路-->stm32篇-->(0)学习路线
stm32·嵌入式硬件·学习
waves浪游2 小时前
进程控制(下)
linux·运维·服务器·开发语言·c++