go语言的context使用

一.定义

context是一个用于跨函数、跨goroutine传递请求范围数据、取消信号以及超时处理的包。
context主要用来做两件事:
1.安全传递数据:是指在请求执行上下文中线程安全地传递数据,依赖于WithValue方法。
2.控制链路

二.context包----核心方法

context包的核心API有四个:
1.context.WithValue: 设置键值对,并且返回一个新的context实例
2.context.WithVancel:
3.context.WithDeadline
4.context.WithTimeout: 三这都返回一个可取消的context实例,和取消函数
注意:context实例是不可变的,每一次都是新创建的

在这里插入图片描述

三.context包----Context接口

context接口核心API有四个:
1.Deadline: 返回过期时间,如果ok为false,说明没有设置过期时间
2.Done: 返回一个channel,一般用于监听Context实例的信号,比如说过期,或者说正常关闭
3.Err: 返回一个错误用于表达Context发生了什么。Canceled => 正常关闭,DeadlineExceeded => 过期超时。
4.Value:取值

在这里插入图片描述

四.使用案例

import (
	"context"
	"testing"
	"time"
)

type mykey struct{}

func TestContext(t *testing.T) {
	// 一般是链路起点,或者调用的起点
	ctx := context.Background()
	// 在你不确定 context 该调用啥的时候 用 TODO()
	//ctx1 := context.TODO()

	ctx = context.WithValue(ctx, mykey{}, "my-value")
	//第二种用法 ctx = context.WithValue(ctx, "my-key", "my-value")

	// 取值, 方式一,直接取出来断言类型 -- 容易panic,因为容易没有设置key和value导致断言失败
	val := ctx.Value(mykey{}).(string)
	t.Log(val)

	// 方式二:推荐
	newVal := ctx.Value("不存在的key")
	val, ok := newVal.(string)

	if !ok {
		t.Log("类型不对")
		return
	}
	t.Log(val)
}

func TestContext_WithCancel(t *testing.T) {
	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	// cancel是一个函数,用于放弃一个正在使用的context,只有第一次调用的时候有用
	// A CancelFunc tells an operation to abandon its work.
	// A CancelFunc does not wait for the work to stop.
	// A CancelFunc may be called by multiple goroutines simultaneously.
	// After the first call, subsequent calls to a CancelFunc do nothing.

	// 用完 ctx 再去调用
	// defer cancel()
	go func() {
		time.Sleep(time.Second)
		cancel()
	}()

	// Done(): 返回一个channel, 一般用于监听Context实例的信号,比如说过期或者正常关闭
	<-ctx.Done()

	// Err(): 返回一个错误用于表达Context发生了什么。 Canceled => 正常关闭,DeadlineExceeded => 过期超时
	t.Log("hello, cancel: ", ctx.Err())
}

func TestContext_WithDeadline(t *testing.T) {
	ctx := context.Background()
	ctx, cancel := context.WithDeadline(ctx, time.Now().Add(time.Second*3))

	// Deadline(): 返回过期时间,如果ok为false,说明没有设置过期时间
	deadline, _ := ctx.Deadline()
	t.Log("deadline: ", deadline)

	defer cancel()
	<-ctx.Done()
	t.Log("hello, timeout: ", ctx.Err())
}

func TestContext_WithTimeout(t *testing.T) {
	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, time.Second*3)
	deadline, _ := ctx.Deadline()
	t.Log("deadline: ", deadline)
	defer cancel()

	<-ctx.Done()
	t.Log("hello, timeoutL ", ctx.Err())
}

// context的实例之间存在父子关系:
// 1.当父亲取消或者超时,所有派生的子context都被取消或者超时
// 2.当找key的时候, 子context先看自己有没有,没有则去祖先里面找
// 控制是从上到下,查找是从下至上的
func TestContext_Parent(t *testing.T) {
	ctx := context.Background()
	parent := context.WithValue(ctx, "my-key", "my value")
	child := context.WithValue(parent, "my-key", "my new value")

	// 查找keu的时候,子类会先看自己有没有,没有才会往祖先里面查找
	t.Log("parent my-key: ", parent.Value("my-key"))
	t.Log("child my-key: ", child.Value("my-key"))
	//    context_test.go:87: parent my-key:  my value
	//    context_test.go:88: child my-key:  my new value

	child2, cancel := context.WithTimeout(parent, time.Second)
	defer cancel()
	t.Log("child2 my-key: ", child2.Value("my-key"))
	//    context_test.go:92: child2 my-key:  my value

	// 一般情况,父类无法获取子类的key,
	child3 := context.WithValue(parent, "new-key", "child3 value")
	t.Log("parent new-key: ", parent.Value("new-key"))
	t.Log("child3 new-key: ", child3.Value("new-key"))
	//    context_test.go:95: parent new-key:  <nil>
	//    context_test.go:96: child3 new-key:  child3 value

	// 逼不得已使用一个map
	parent1 := context.WithValue(ctx, "map", map[string]string{})
	child4, cancel := context.WithTimeout(parent1, time.Second)
	defer cancel()

	m := child4.Value("map")
	m1, ok := m.(map[string]string)
	if ok {
		m1["key1"] = "value1"
	}
	nm := parent1.Value("map")
	nm1, ok := nm.(map[string]string)
	t.Log("parent1 key1: ", nm1["key1"])
	//    context_test.go:110: parent1 key1:  value1

}

五.context包----valueCtx实现

valueCtx用于存储key-value数据,特点:
典型的装饰器模式:在已有Context接口的基础上附加一个存储 key-value的功能
只能存储一个key,value

在这里插入图片描述

六.context包----控制

context包提供了三个控制方法,WithCancel、WithDeadline和WithTimeout。三者用法大同小异:
1.没有过期时间,但是有需要在必要的时候取消,使用WithCancel
2.在固定时间点过期,使用WithDeadline
3.在一段时间后过期,使用WithTimeout
而后便是监听Done()返回的channel,不管是主动调用cancel()还是超时,都能从这个cannel里面取出来数据。后面可以用Err()方法来判断究竟是哪种情况。
父亲可以控制儿子,但是儿子控制不了父亲。(父亲拥有主动取消和设置取消时间的权限,儿子只能在父亲设置的取消时间范围内取消,父亲设置3秒超时取消,儿子不能设置超过3秒,但是可以设置小于3秒的过期取消时间)	

在这里插入图片描述

七.context包----time.AfterFunc

在这里插入图片描述

八.context包----cancelCtx实现

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

九.context包----timerCtx实现

在这里插入图片描述
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值