【Golang包】Golang中的Context具体实现
🔗【Golang包】Golang中的Context具体实现
2022-10-13
| 2023-10-15
0  |  0 分钟
type
status
date
slug
summary
tags
category
icon
password
Sub-item
Last edited time
Oct 15, 2023 03:57 AM
Parent item
领域

根Context

有一个emptyCtx结构实现Context的方法,基于该结构, 定义了2个全局变量为根Context。
其中:
  • Background通常被用于主函数、初始化以及测试中,作为一个顶层的context,一般我们创建的context都是基于Background
  • TODO是在不确定使用什么context的时候才会使用。

valueCtx

valueCtx是带有存储key-vaule功能的Context
WithValue
WithValue用以向context添加键值对:
添加后会形成一个Context链:
notion image

cancelCtx

其中:
  • done表示一个channel,用来表示传递关闭信号;
  • children表示一个map,存储了当前context节点下的子节点;
  • err用于存储错误信息表示任务结束的原因。
再来看一下cancelCtx实现的方法:
可以发现cancelCtx类型变量其实也是canceler类型,因为cancelCtx实现了canceler接口。 
cancelCtx类型的context在调用cancel方法时会设置取消原因,将done channel设置为一个关闭channel或者关闭channel,然后将子节点context依次取消,如果有需要还会将当前节点从父节点上移除。
WithCancel
WithCancel函数用来创建一个可取消的context,即cancelCtx类型的contextWithCancel返回一个context和一个CancelFunc,调用CancelFunc即可触发cancel操作。直接看源码:
之前说到cancelCtx取消时,会将后代节点中所有的cancelCtx都取消,propagateCancel即用来建立当前节点与祖先节点这个取消关联逻辑。
  1. 如果parent.Done()返回nil,表明父节点以上的路径上没有可取消的context,不需要处理;
  1. 如果在context链上找到到cancelCtx类型的祖先节点,则判断这个祖先节点是否已经取消,如果已经取消就取消当前节点;否则将当前节点加入到祖先节点的children列表。
  1. 否则开启一个协程,监听parent.Done()child.Done(),一旦parent.Done()返回的channel关闭,即context链中某个祖先节点context被取消,则将当前context也取消。
这里或许有个疑问,为什么是祖先节点而不是父节点?这是因为当前context链可能是这样的:
notion image
当前cancelCtx的父节点context并不是一个可取消的context,也就没法记录children

timerCtx

timerCtx是一种基于cancelCtxcontext类型,从字面上就能看出,这是一种可以定时取消的context
timerCtx内部使用cancelCtx实现取消,另外使用定时器timer和过期时间deadline实现定时取消的功能。timerCtx在调用cancel方法,会先将内部的cancelCtx取消,如果需要则将自己从cancelCtx祖先节点上移除,最后取消计时器。

WithDeadline

WithDeadline返回一个基于parent的可取消的context,并且其过期时间deadline不晚于所设置时间d
  1. 如果父节点parent有过期时间并且过期时间早于给定时间d,那么新建的子节点context无需设置过期时间,使用WithCancel创建一个可取消的context即可;
  1. 否则,就要利用parent和过期时间d创建一个定时取消的timerCtx,并建立新建context与可取消context祖先节点的取消关联关系,接下来判断当前时间距离过期时间d的时长dur
  1. 如果dur小于0,即当前已经过了过期时间,则直接取消新建的timerCtx,原因为DeadlineExceeded
  1. 否则,为新建的timerCtx设置定时器,一旦到达过期时间即取消当前timerCtx

WithTimeout

WithDeadline类似,WithTimeout也是创建一个定时取消的context,只不过WithDeadline是接收一个过期时间点,而WithTimeout接收一个相对当前时间的过期时长timeout:
软件开发
  • GoLang
  • Golang包
  • 【Flow】编码风格指南【Golang高级特性】struct 嵌入 interface
    目录