golang context使用小结

Go标准库中的context包,提供了goroutine之间的传递信息的机制,信号同步,除此之外还有超时(timeout)和取消(cancel)机制。概括起来,Context可以控制子goroutine的运行,超时控制的方法调用,可以取消的方法调用。

context核心数据结构

  1. Context interface
type Context interface {
  Deadline() (deadline time.Time, ok bool) 
  // 返回这个Context被取消的截止时间,如果没有设置截止时间,ok的值返回的是false
  Done() <-chan struct{}                   
  // 返回一个只读的channel 一般用来监听context的取消 当context关闭后,Done()返回一个被关闭的管道,关闭的管道仍然是可读的,据此goroutine可以收到关闭请求,当context还未关闭时,Done()返回nil
  Err() error
  // 当context关闭后,Err()返回context的关闭原因 当context还未关闭时,Err()返回nil
  Value(key any) any                       
  // 返回此cxt中指定key对应的value 用于goroutine间传递信息
}
  1. 生产对应的结构体
    context包提供了4个方法创建不同类型的context
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)              // CancelCtx  创建带有取消功能的Context
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)          // DeadLineCtx 创建带有定时自动取消功能的Context
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) // TimeoutCtx 创建带有定时自动取消功能的Context
func WithValue(parent Context, key, val any) Context                          // ValueCtx 带有键值的context
  1. emptyCtx
type emptyCtx int // 是一个空的 context,本质上类型为一个整型 可以由 context.Background() 或 context.TODO() 获取

var (
	background = new(emptyCtx)
	todo       = new(emptyCtx)
)
// context.Background() 和 context.TODO() 本质上没有什么区别 都用于创建一个默认的、无限期的上下文,只是用于不同场景下
// 通常被用于主函数、初始化以及测试中,作为一个顶层的context,也就是说一般我们创建的context都是基于Background
// 而TODO是在不确定使用什么context的时候才会使用

使用

1.WithCancel

package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	go gOne(ctx, "任务一")
	time.Sleep(time.Second * 10)
	cancel()
	time.Sleep(time.Second * 2)
	fmt.Println("exit")
}

func gOne(ctx context.Context, name string) {
	go gTwo(ctx, "任务二")
	for {
		select {
		case <-ctx.Done():
			fmt.Println(name + ":任务已停止")
			return
		case <-time.After(time.Second * 1):
			fmt.Println(name + "任务执行中")
		}
	}
}

func gTwo(ctx context.Context, name string) {
	for {
		select {
		case <-ctx.Done():
			fmt.Println(name + ":任务已停止")
			return
		case <-time.After(time.Second * 2):
			fmt.Println(name + "任务执行中")
		}
	}
}

2.WithDeadline WithTimeout
这两个方法本质是一样的 WithTimeout也是调用的WithDeadline方法

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
	return WithDeadline(parent, time.Now().Add(timeout))
}
package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	ctx, _ := context.WithTimeout(context.Background(), time.Second*5)

	go gTimeOne(ctx, "任务一")

	time.Sleep(10 * time.Second)
	fmt.Println("exit")
}

func gTimeOne(ctx context.Context, name string) {
	go gTimeTwo(ctx, "任务二")
	for {
		select {
		case <-ctx.Done():
			fmt.Println(name + ":任务已停止")
			return
		case <-time.After(time.Second * 1):
			fmt.Println(name + "任务执行中")
		}
	}
}

func gTimeTwo(ctx context.Context, name string) {
	for {
		select {
		case <-ctx.Done():
			fmt.Println(name + ":任务已停止")
			return
		case <-time.After(time.Second * 2):
			fmt.Println(name + "任务执行中")
		}
	}
}

3.WithValue
使用valueContext在协程之间共享数据

package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	ctx := context.WithValue(context.Background(), "param", "1234")

	go gWork(ctx)

	time.Sleep(time.Second * 5)
	fmt.Println("exit")
}

func gWork(ctx context.Context) {
	value := ctx.Value("param").(string)
	fmt.Println(value)
}

由于valueCtx不具有Done() 相关方法的实现,一般是配合另几个方法使用

package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	valueCtx := context.WithValue(ctx, "param", "1234")

	go gWork2(valueCtx)
	time.Sleep(time.Second * 5)
	cancel()

	time.Sleep(time.Second * 10)
	fmt.Println("exit")
}

func gWork2(ctx context.Context) {
	value := ctx.Value("param").(string)
	fmt.Println(value)
	for {
		select {
		case <-ctx.Done():
			fmt.Println("任务已停止")
			return
		case <-time.After(time.Second * 2):
			fmt.Println("任务执行中")
		}
	}
}

gin中context的使用

gin接口超时处理
1.利用context.WithTimeout的特性去实现

// 入侵式
https://gist.github.com/montanaflynn/ef9e7b9cd21b355cfe8332b4f20163c1
// 非入侵式,无须修改原来代码(可能有bug)
https://vearne.cc/archives/39130

2.官方提供的插件

https://github.com/gin-contrib/timeout

grpc中context的使用

// 利用context做grpc超时处理
https://www.cnblogs.com/rickiyang/p/15049307.html
// grpc系列文章
https://zhuanlan.zhihu.com/p/612406500
// grpc超时传递原理
https://xiaomi-info.github.io/2019/12/30/grpc-deadline/
posted @   元気田支店长  阅读(62)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示