关于什么为上下文机制
一般来说,我们如果想要在多个进程中监听彼此,最常用的方法就是使用管道进行监听
例如最常用的,想要在进程之间传递某个进程已经完成的信号,我们经常使用通道的方式进行传递消息.
举个例子,一个进程B想要监听另一个进程A,可以通过一个管道进行监听,B中使用for和select结合来监听管道是否关闭.A进程执行完自己的任务以后,对管道进行close操作,此时B即可得知A的关闭
这种操作方法不是很优雅,而且会遇到一些特殊的情况,比如想要监听某个嵌套进程?这就会很麻烦
上面这种情况,就是上下文之一的"带有取消类型的上下文"的使用情景,接下来我们一共会介绍四种类型的上下文
在其他语言中,上下文对象指的是调用某个方法,属性的背景,通常指的是this这种东西
但是在golang中,Context
译为上下文,是Go提供的一种并发控制的解决方案,相比于管道和WaitGroup
,它可以更好的控制子孙协程以及层级更深的协程。Context
本身是一个接口,只要实现了该接口都可以称之为上下文例如著名Web框架Gin
中的gin.Context
。context
标准库也提供了几个实现,分别是:
emptyCtx
cancelCtx
timerCtx
valueCtx
他们都是基于一个接口实现的,该接口中一共有四个基本方法 ,根据不同的上下文对象的细分类型使用
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key any) any
}
//上下文的几种类型
//首先context这个接口,以及一个儿子三个孙子,都完成了四个方法
//1.deadline 确定一个ddl
//2.Done 返回一个只读流,这个可以监听接口是否被取消
//3.Err 返回一个错误类型
//4.Value 内嵌一个键,可以返回一个数据
//这几个方法对应了不同的类型
可以通过如下两种方法直接调用空的ctx对象
这两种方法创建出的空对象页经常作为下面三种的父上下文
func TestEmptyCtx() {
//第一种类型,空的context对象,没啥东西
//两种方式直接返回空的emptyCtx对象
context.Background()
context.TODO()
}
valueCtx,主要用来传递一些数据
一个valueCtx对象能保存一共键值对
func TestValueCtx() {
//ctx主要用来在协程和子协程中传递数据
ctx := context.WithValue(context.Background(), "key", "value")
go func(ctx context.Context) {
//读取ctx中的东西
fmt.Println("valueCtx中的数值为", ctx.Value("key"))
}(ctx)
//下面暂时休眠一秒,让主线程能正常运行
time.Sleep(time.Second)
}
//valueCtx的内部实现很简单,里面只有三个属性
//context key value 只能存储一个键值对
//实现了方法 Value(key),如果在这个上下文找不到,就会往父类的上下文找
带有取消方法的ctx对象,在创建的时候会自动生成一共cancel方法
一旦调用这个方法,ctx对象中的Done管道就会被截止,其他进程就可以监听到这个上下文消息的结束
func TestCancelCtx() {
ctx, cancel := context.WithCancel(context.Background())
go func(ctx context.Context) {
fmt.Println("正在取消上下文")
time.Sleep(time.Second)
cancel()
}(ctx)
for {
select {
case <-ctx.Done():
{
fmt.Println("检测到上下文被关闭")
return
}
}
}
}
在原本的带有取消方法的基础上,新增了定时
func TestTimerCtx() {
//timerCtx的创建有两种函数
//withDeadline(.....,具体截止时间) //比如某年某月某分某秒
//withTimeout(.....,手动定义时间间隔) //比如五分钟
//这里手动设置过期时间为一秒
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Second))
defer cancel()
//返回的两个东西,cancel仍然是可以手动进行取消
//ctx是内置的退休时间的
wait := sync.WaitGroup{}
wait.Add(1)
go func(ctx context.Context, wait *sync.WaitGroup) {
count := 1
for {
select {
case <-ctx.Done():
{
fmt.Println("寄了")
wait.Done()
return
}
default:
fmt.Println("你要黄的粉的", count)
count += 1
}
}
}(ctx, &wait)
wait.Wait()
}