go通过context.WithValue传递指针

日期 2022年10月19日 11:17

分类 Golang

标签

浏览 4164

字数统计: 2171(字)

文章目录

Go - Context: 信息穿透上下文

使用场景

  • 上下文传递信息(request-scoped),比如处理http请求、在请求处理链路上传递信息;
  • 控制子goroutine的运行;
  • 超时控制的方法调用(timeout);
  • 可以取消的方法调用(cancel);

WithValue-传递上下文

以下是传递上下文方式

ctx = context.TODO()
ctx = context.WithValue(ctx, "key1", "0001")
ctx = context.WithValue(ctx, "key2", "0001")
ctx = context.WithValue(ctx, "key3", "0001")
ctx = context.WithValue(ctx, "key4", "0004")

fmt.Println(ctx.Value("key1"))

WithValue-通过透传指针参数(ctx.Value的值需要变化)

context通过透传指针参数可便于函数内外部修改与接收
应用场景: (ctx.Value的值需要变化)

  • 外部修改ctx.Value的值: 内部可以获取参数的值改变(比如需提前cancel并需改变某个透传的值)
  • 内部修改ctx.Value的值: 外部可以获取参数的值改变(内部执行到某位置并修改了透传的值)

示例

package main

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

// golang-context通过透传指针参数可便于函数内外部修改与接收
// 应用场景: (ctx.Value的值需要变化)
// 外部修改ctx.Value的值: 内部可以获取参数的值改变(比如需提前cancel并需改变某个透传的值)
// 内部修改ctx.Value的值: 外部可以获取参数的值改变(内部执行到某位置并修改了透传的值)

func main() {
	// 初始cancel context
	ctx, cancel := context.WithCancel(context.Background())

	// 预先设置传递参数
	params := make([]string, 0)
	results := make([]any, 0)
	ctx = context.WithValue(ctx, "params", &params)   // 指针,调用内修改,外部直接使用其值
	ctx = context.WithValue(ctx, "results", &results) // 指针,外部直接修改,调用内获取值

	// 手动触发cancel
	go func() {
		time.Sleep(1 * time.Second)

		// 这时ctx.Value("params")已被函数内修改,newParams和params一致
		fmt.Println("params", params)
		newParams := ctx.Value("params").(*[]string)
		fmt.Println("newParams", newParams)

		// 直接修改results,这时ctx.Value("results")已修改
		results = []any{"1", "2", "3"}
		fmt.Println("results", results)

		// 调用cancel,提前结束testContextValue
		cancel()
	}()

	// 执行
	newResults := testContextValue(ctx, 20*time.Second)
	// 结果 &["1", "2", "3"]
	fmt.Println("newResults", newResults)
}

// 应用场景
func testContextValue(ctx context.Context, timeout time.Duration) (resp *[]any) {
	// code before ...

	// 测试修改Context参数值
	oldIds, ok := ctx.Value("params").(*[]string)
	if ok {
		*(oldIds) = []string{"a", "b", "c"}
	}

	// 阻塞或提前cancel
	select {
	case <-ctx.Done(): // 主动cancel
		// 主动调用cancelFunc前可先更改context参数值,
		// 主动停止时设置响应结果,通过context接收数据
		resp, _ = ctx.Value("results").(*[]any)
	case <-time.After(timeout): // 阻塞
	}

	// after code ...

	return
}