Golang底层原理剖析之上下文Context

简介: Golang底层原理剖析之上下文Context

前言

如何优雅地使用context点击浅谈Golang上下文Context

Context


在Go语言并发编程中,用一个goroutine来处理一个任务 ,而它又会创建多个goroutine来负责不同子任务的创建非常常见,这些场景中往往会需要在API边界之间以及过程之间,传递截止时间,取消信号,或其他与请求相关的数据,这时候就可以使用Context,context包在Go1.7的时候被加入到官方库中。

主要内容可以概括为一个接口,四种具体实现,还有六个函数。


Context接口提供了四个方法,emptyCtx本质上是个整型,*emptyCtx对Context接口的实现只是简单的返回nil,false等等,时机实际上什么也没做。Backgroud和TODO这两个函数内部都会创建emptyCtx。

其中Background主要用于在初始化时获取一个Context,这里变量ctx是非空接口类型。还记得非空接口的结构吗,itab这里的接口类型为Context,动态类型为*emptyCtx,data指向一个emptyCtx,本质上就是一个int,这就是Background函数返回的变量结构。


而TODO函数,官方文档建议在本来应该使用外层传递的ctx,而外层却没有传递的地方使用,就像函数名称表达的含义一样,留下一个TODO。



再来看cancelCtx类型,这是一种可取消的Context。


done用于获取该Context的取消通知

children用于存储以当前节点为根节点的所有可取消的Context。以便在根节点取消时,可以把它们一并取消。

err用于存储取消时指定的错误信息,

而mu就是用来保护这几个字段的锁,以保障cancelCtx是线程安全的


而WitchCancel函数可以把一个Context包装为cancelCtx,并提供一个取消函数,调用它可以取消(cancel)对应的Context。

例如这里,我们基于ctx创建一个cancelCtx,ctx1也是非空接口,itab这里的接口类型是Context,动态类型为*cancelCtx,data指向一个cancelCtx结构体,第一个字段存储的是它的父级Context

再来看timerCtx,它在cancelCtx的基础上,又封装了一个定时器和一个截止时间,这样既可以根据需要主动取消,也可以在到达deadline时,通过timer来触发取消动作。要注意这个timer也会由cancelCtx.mu来保护,确保取消操作也是线程安全的。通过WithDeadline和WithTimeout函数,都可以创建timerCtx,区别是WithDeadline函数需要制定一个时间点,而WithTimeout函数接受一个时间段。


接下来我们使用WithDeadline基于ctx1构造一个timerCtx, ctx2同样是Context接口类型,动态类型为*timerCtx,data指向一个timerCtx结构体。第一个字段是一个cancelCtx,这个cancelCtx是基于ctx1创建的。后面是一个定时器和一个截止时间。这个定时器会在deadline到达时调用cancelCtx的取消函数。



现在可以看到ctx2是基于ctx1创建的,ctx1是又是基于ctx创建的,基于每个Context可以创建多个Context,这样就形成了一颗Context数,每个节点都可以由零个或多个子节点,可取消的Context都会被注册到离他最近的,可取消的祖先节点中。对ctx2来说离他最近的,可取消的祖先节点是ctx1,所以在ctx1这里的children map中,会增加ctx2这组键值对。


如果ctx2先取消,就只会影响到以它为根节点的Context,而如果是ctx1先取消,就可以根据children map中的记录,把ctx1子节点中所以可取消的Context全部cancel掉


最后来看valueCtx类型,它用来支持键值对打包,WithValue函数,可以给Context附加一个键值对信息,这样就可以通过Context传递数据了

现在我们给ctx附加一个键值对,<keyA,valA>,变量ctxA也是Context接口类型 ,动态类型为*valueCtx,data指向一个valueCtx结构体。第一个字段是它的父级Context,key和val字段都是空接口类型。key的动态类型为string,动态值是string类型的变量“keyA”,val的动态类型同样是string, 动态值为“valA”。



下面我们再基于ctxA,附加一个key相等但是val不相等的键值对。ctxC的动态值指向这样一个valueCtx,父级Context自然是ctxA,key的动态类型为string,动态值是string类型的变量"keyA",与ctxA中的相同,但是val的值与ctxA中的不相等,通过ctxC获取keyA和keyC对应的值时,会发现keyC覆盖了keyA对应的val


要找到原因,就要先看看Value的方法是怎样工作的,首先它会比较当前Context中的key是否等于要查找的key,因为keyA等于keyC,所以keyA的查找会直接锁定到ctxC这里的val,因而出现了子节点覆盖父节点数据的情况




为了规避这种情况,最好不要直接使用string,int这些基础类型作为key,而是用自定义类型包装一下。就像这样,把keyA定义为keytypea类型,keyC定义为keytypeC类型,这样再次通过ctxC获取keyA时,因为key的类型不相同,第一步key相等性的比较不通过,就会委托父节点继续查找,进而找到正确的val。



所以说valueCtx之间通过Context字段,形成了一个链表结构,使用Context传递数据时还要注意,Context本身是本着不可改变(immutable)的模式设计的,所以不要试图修改ctx里面保存的值



在http,sql相关的库中,都提供了对Context的支持,方便我们在处理请求时,实现超时自动取消,或传递请求相关的控制数据等等


目录
相关文章
|
Java Go
Golang底层原理剖析之垃圾回收GC(二)
Golang底层原理剖析之垃圾回收GC(二)
235 0
|
11月前
|
存储 安全 测试技术
GoLang协程Goroutiney原理与GMP模型详解
本文详细介绍了Go语言中的Goroutine及其背后的GMP模型。Goroutine是Go语言中的一种轻量级线程,由Go运行时管理,支持高效的并发编程。文章讲解了Goroutine的创建、调度、上下文切换和栈管理等核心机制,并通过示例代码展示了如何使用Goroutine。GMP模型(Goroutine、Processor、Machine)是Go运行时调度Goroutine的基础,通过合理的调度策略,实现了高并发和高性能的程序执行。
636 29
|
11月前
|
负载均衡 算法 Go
GoLang协程Goroutiney原理与GMP模型详解
【11月更文挑战第4天】Goroutine 是 Go 语言中的轻量级线程,由 Go 运行时管理,创建和销毁开销小,适合高并发场景。其调度采用非抢占式和协作式多任务处理结合的方式。GMP 模型包括 G(Goroutine)、M(系统线程)和 P(逻辑处理器),通过工作窃取算法实现负载均衡,确保高效利用系统资源。
239 3
|
存储 关系型数据库 Go
SOLID原理:用Golang的例子来解释
SOLID原理:用Golang的例子来解释
|
算法 NoSQL 关系型数据库
熔断原理与实现Golang版
熔断原理与实现Golang版
|
存储 人工智能 Go
golang 反射基本原理及用法
golang 反射基本原理及用法
100 0
|
人工智能 Go
Golang 里的 context
Golang 里的 context
76 0
|
负载均衡 监控 Go
Golang深入浅出之-Go语言中的服务网格(Service Mesh)原理与应用
【5月更文挑战第5天】服务网格是处理服务间通信的基础设施层,常由数据平面(代理,如Envoy)和控制平面(管理配置)组成。本文讨论了服务发现、负载均衡和追踪等常见问题及其解决方案,并展示了使用Go语言实现Envoy sidecar配置的例子,强调Go语言在构建服务网格中的优势。服务网格能提升微服务的管理和可观测性,正确应对问题能构建更健壮的分布式系统。
600 1
|
JSON 监控 安全
Golang深入浅出之-Go语言中的反射(reflect):原理与实战应用
【5月更文挑战第1天】Go语言的反射允许运行时检查和修改结构,主要通过`reflect`包的`Type`和`Value`实现。然而,滥用反射可能导致代码复杂和性能下降。要安全使用,应注意避免过度使用,始终进行类型检查,并尊重封装。反射的应用包括动态接口实现、JSON序列化和元编程。理解反射原理并谨慎使用是关键,应尽量保持代码静态类型。
246 2
|
数据管理 Go 开发者
Golang深入浅出之-Go语言上下文(context)包:处理取消与超时
【4月更文挑战第25天】Go语言中的`context`包在并发、网络请求和长任务中至关重要,提供取消、截止时间和元数据管理。本文探讨`context`基础,如`Background()`、`TODO()`、`WithCancel()`、`WithDeadline()`和`WithTimeout()`。常见问题包括不当传递、过度使用`Background()`和`TODO()`以及忽略错误处理。通过取消和超时示例,强调正确传递上下文、处理取消错误和设置超时以提高应用健壮性和响应性。正确使用`context`是构建稳定高效Go应用的关键。
367 1

热门文章

最新文章

推荐镜像

更多