Golang基础笔记十三之context
本文首发于公众号:Hunter后端
原文链接:Golang基础笔记十三之context
在 Golang 里,context 包提供了很多比如传递截止时间、取消信号、传递数据等操作的标准方式,用于在跨 API 边界、进程和 goroutine之间进行。
这一篇笔记详细介绍一下 context 包相关的一些操作。
以下是本篇笔记目录:
- Context 接口及作用
- 取消传播
- 超时控制
- 截止时间
- 传递数据
1、Context 接口及作用
1. Context 接口
Context 是 context 包下的一个接口,其定义如下:
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key any) any
}
- Deadline() 返回上下文被取消的时间
- Done() 返回一个通道,当上下文被取消时关闭
- Error() 返回上下文被取消的原因
- Value() 可以获取相应的键值对数据
下面所有的操作都是基于 context.Context 实现。
2. context 相关函数
我们可以使用 context 创建一些函数,用来实现取消、超时控制、传递数据等操作。
1) context.Background()
根上下文,可以作为所有上下文的起点,当我们需要实现取消、超时控制等功能,都需要定义一个父级上下文,这个时候我们就可以使用 context.Backgroud() 来创建。
2) context.TODO()
当我们不确定使用哪个上下文时,就可以使用 context.TODO(),但是在源代码中,它的实现与 context.Backgroud() 是一样的逻辑。
3) context.WithCancel(parent)
创建可以取消的上下文,参数是父级上下文,当我们调用一个函数,并且希望在某些时候取消这个调用,比如超时,这个时候我们可以使用这个函数,并手动进行取消。
4) context.WithTimeout(parent, timeout)
创建有超时的上下文,参数是父级上下文和 time.Duration,当我们调用一个函数,并且希望在多久以后可以自动取消,可以使用这个函数。
5) context.WithDeadline(parent, d)
创建有截止时间的上下文,参数是父级上下文和 time.Time,当我们调用一个函数,并且希望在某个具体的时间点可以自动取消,可以使用这个函数。
6) context.WithValue(parent, key, value)
创建带有键值对的上下文,我们希望通过上下文传递数据,就可以使用这个函数。
这里介绍了 context 相关的一些函数,接下来我们以具体的代码为示例,分别用这些函数来实现对应的功能。
2、 取消传播
我们使用 context.WithCancel() 函数实现取消传播的功能。
其实就是取消函数执行的操作,为什么会说取消传播,因为上下文可以在函数调用中一层一层传递,当我们取消了根上下文,所有调用链中的上下文都会被取消。
context.WithCancel() 的使用代码示例如下:
ctx, cancel := context.WithCancel(context.Background())
这个函数返回两个结果,一个是上下文参数,一个是取消函数,我们可以使用取消函数在特定节点取消这个上下文,这里取消操作需要我们手动执行。
下面我们实现一个取消操作,我们调用两个函数,这两个函数随机执行一段时间,然后某个函数先返回结果,返回之后我们立马执行上下文的取消操作,
package main
import (
"context"
"fmt"
"math/rand"
"time"
)
func F1(ctx context.Context, ch chan string) {
sleepSeconds := rand.Intn(3)
time.Sleep(time.Duration(sleepSeconds) * time.Second)
ch <- "f1 result"
}
func F2(ctx context.Context, ch chan string) {
sleepSeconds := rand.Intn(3)
time.Sleep(time.Duration(sleepSeconds) * time.Second)
ch <- "f2 result"
}
func CallF1(ctx context.Context, ch chan string) {
chF1 := make(chan string)
go F1(ctx, chF1)
select {
case result := <-chF1:
ch <- result
case <-ctx.Done():
fmt.Println("F1 函数调用超时")
}
}
func CallF2(ctx context.Context, ch chan string) {
chF2 := make(chan string)
go F2(ctx, chF2)
select {
case result := <-chF2:
ch <- result
case <-ctx.Done():
fmt.Println("F2 函数调用超时")
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ch1 := make(chan string)
ch2 := make(chan string)
go CallF1(ctx, ch1)
go CallF2(ctx, ch2)
select {
case r1 := <-ch1:
fmt.Println("f1 调用完成: ", r1)
cancel()
case r2 := <-ch2:
fmt.Println("f2 调用完成: ", r2)
cancel()
}
}
在这里,我们目标调用函数为 F1() 和 F2(),使用 CallF1() 和 CallF2() 作为中间调用函数,在其中使用 select-case 等待目标函数返回结果,并同时监听 ctx.Done() 判断 ctx 是否已经取消。
同时在 main() 函数中监听 ch1 和 ch2 判断哪个通道先返回结果,监听到返回结果马上取消 ctx 上下文,main() 函数就可以接着往下执行,避免两个目标函数其中一个长时间执行。
这里需要注意的是,中间函数 CallF1() 和 CallF2() 虽然被取消了,但 F1()、F2() 作为执行的 goroutine 并没有取消执行,如果有取消的需求,可以在 F() 函数内部伺机监听 ctx.Done() 以提前退出函数。
3、 超时控制
我们使用 context.WithTimeout(parent, timeout) 可以实现超时控制。
下面实现一个功能为,调用某个函数,并给一秒的超时时间,超过时间则进行超时处理,避免长时间堵塞,其中目标执行函数为 TargetFunc,其中,随机 sleep 三秒内的时间。
整体代码如下:
package main
import (
"context"
"fmt"
"math/rand"
"time"
)
func TargetFunc() string {
sleepSeconds := rand.Intn(3)
time.Sleep(time.Duration(sleepSeconds) * time.Second)
return "result"
}
func CallFunc(ch chan string) {
ch <- TargetFunc()
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
ch := make(chan string)
defer close(ch)
go CallFunc(ch)
select {
case result := <-ch:
fmt.Println("result: ", result)
case <-ctx.Done():
fmt.Println("函数调用超时")
}
}
其中,TargetFunc 函数内部的逻辑为随机休息三秒内的时间,可能导致超时,也可能不超时。
CallFunc() 函数用作中间函数,用于获取目标函数调用结果并通过 channel 返回。
在 main 函数中,首先定义一个有超时时间的上下文,设置了一秒超时,然后定义一个通道,将其传给 CallFunc() 用于返回数据。
之后通过 select-case 操作,进入循环等待状态,用于判断 goroutine 中的 channel 和 ctx 的超时哪个先返回结果。
如果 TargetFunc 函数调用时间过长,ctx 超时控制的上下文先到期,那么则会打印出 函数调用超时 的信息,否则会打印出函数调用返回的结果。
4、 截止时间
我们可以使用 context.WithDeadline(parent, d) 函数实现一个有截止时间的上下文,它的调用参数第二个为 d,是一个具体的 time.Time 类型。
但其实,在背后的源码中,context.WithTimeout() 函数会将其中的 time.Duration 参数处理通过 time.Now().Add(timeout) 的方式处理成 time.Time 然后再调用 WithDeadline 函数,所以超时控制和截止时间这两个函数在本质上的逻辑是一致的。
所以这里我们的代码示例,也只是把输入的参数修改一下,即可实现功能,如下:
deadline := time.Now().Add(1 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
5、 传递数据
我们可以通过 context.WithValue() 函数实现传递数据的功能,这个函数接收三个参数,父级上下文,key 和 value,以下是使用示例:
ctx := context.Background()
ctxWithValue := context.WithValue(ctx, "str1", "value1")
ctxWithValue = context.WithValue(ctxWithValue, "slice", []int{1, 2, 3})
在获取数据的时候,我们可以使用 ctx.Value() 函数获取:
func ProcessCtxValue(ctx context.Context) {
value1 := ctx.Value("str1")
fmt.Println("str1 value: ", value1)
value2 := ctx.Value("slice")
fmt.Println("slice value: ", value2)
}
但是在真正用到这些数据的时候,我们还需要对 value 进行类型判断,可以直接如下操作:
func ProcessCtxValue(ctx context.Context) {
value1, ok := ctx.Value("str1").(string)
if ok {
fmt.Println("str1 value: ", value1)
}
value2 := ctx.Value("slice").([]int)
if ok {
fmt.Println("slice value: ", value2, value2[1])
}
}
Golang基础笔记十三之context的更多相关文章
- Golang基础笔记
<基础> Go语言中的3个关键字用于标准的错误处理流程: defer,panic,recover. 定义一个名为f 的匿名函数: Go 不支持继承和重载. Go的goroutine概念:使 ...
- MYSQL基础笔记(五)- 练习作业:站点统计练习
作业:站点统计 1.将用户的访问信息记录到文件中,独占一行,记录IP地址 <?php //站点统计 header('Content-type:text/html;charset=utf-8'); ...
- java之jvm学习笔记十三(jvm基本结构)
java之jvm学习笔记十三(jvm基本结构) 这一节,主要来学习jvm的基本结构,也就是概述.说是概述,内容很多,而且概念量也很大,不过关于概念方面,你不用担心,我完全有信心,让概念在你的脑子里变成 ...
- golang学习笔记8 beego参数配置 打包linux命令
golang学习笔记8 beego参数配置 打包linux命令 参数配置 - beego: 简约 & 强大并存的 Go 应用框架https://beego.me/docs/mvc/contro ...
- go语言,golang学习笔记1 官网下载安装,中文社区,开发工具LiteIDE
go语言,golang学习笔记1 官网下载安装,中文社区,开发工具LiteIDE Go语言是谷歌2009发布的专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速 ...
- Golang基础教程
以下使用goland的IDE演示,包含总计的golang基础功能共20个章节 一.go语言结构: 二.go基础语法: 三.变量 四.常量 五.运算符 六.条件语句 七.循环 八.函数 九.变量作用域 ...
- Bootstrap <基础二十三>页面标题(Page Header)
页面标题(Page Header)是个不错的功能,它会在网页标题四周添加适当的间距.当一个网页中有多个标题且每个标题之间需要添加一定的间距时,页面标题这个功能就显得特别有用.如需使用页面标题(Page ...
- Java基础笔记 – Annotation注解的介绍和使用 自定义注解
Java基础笔记 – Annotation注解的介绍和使用 自定义注解 本文由arthinking发表于5年前 | Java基础 | 评论数 7 | 被围观 25,969 views+ 1.Anno ...
- golang基础知识之encoding/json package
golang基础知识之json 简介 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式.可以去json.org 查看json标准的清晰定义.json pack ...
- python3.4学习笔记(十三) 网络爬虫实例代码,使用pyspider抓取多牛投资吧里面的文章信息,抓取政府网新闻内容
python3.4学习笔记(十三) 网络爬虫实例代码,使用pyspider抓取多牛投资吧里面的文章信息PySpider:一个国人编写的强大的网络爬虫系统并带有强大的WebUI,采用Python语言编写 ...
随机推荐
- GUI development with Rust and GTK4 阅读笔记
简记 这是我第二次从头开始阅读,有第一次的印象要容易不少. 如果只关心具体的做法,而不思考为什么这样做,以及整体的框架,阅读的过程将会举步维艰. 简略记录 gtk-rs 的书中提到的点.对同一个问题书 ...
- 【记录】Pandoc|Linux安装最新Pandoc
参考:官方文档,https://pandoc.org/installing.html 系统:Ubuntu 注意!Pandoc 不能接受 PDF 作为输入!!!如果是想下载 Pandoc 做 PDF 格 ...
- 【经验】Ubuntu20.04虚拟机的网络问题|在NAT模式没有网络连接图标,桥接模式正常
我的版本:Ubuntu20.04.04,iso是清华镜像站的release版本. 问题:NAT模式没有网络连接图标,或者图标闪一下就消失不见,并且无法获取IP地址:但是桥接模式的网络服务却是正常的. ...
- JAVA 24 环境安装与配置
JAVA 24 环境安装与配置 一.Java Downloads Java 下载(Windows x64) https://www.oracle.com/java/technologies/downl ...
- python中print函数参数解析
print(*values: object, sep: Optional[Text]=..., end: Optional[Text]=..., file: Optional[_Writer]=... ...
- 单服务器高性能模式:PPC与TPC
极客时间:<从 0 开始学架构>:单服务器高性能模式:PPC与TPC 1.引言 高性能是系统中最复杂的一环,其中磁盘.操作系统.CPU.内存.缓存.网络.编程语言.架构等,每个都有可能影响 ...
- Django中自定义错误处理
1.将项目中的settings.py中的DEBUG=False,ALLOWED_HOSTS = ['localhost'] 2.在项目settings.py中TEMPLATES列表中的DIR定义的路径 ...
- 解锁FastAPI与MongoDB聚合管道的性能奥秘
title: 解锁FastAPI与MongoDB聚合管道的性能奥秘 date: 2025/05/20 20:24:47 updated: 2025/05/20 20:24:47 author: cmd ...
- PLsql and 汉化包的安装使用
一.准备工作 1.点击下载PLSQL,本次安装的PLSQL版本为12.0.7,建议安装64位. 2.下载PLSQL时,版本旁边会有个"Language pack"的链接,点击后左侧 ...
- Java 实现微信小程序不同人员生成不同小程序码并追踪扫码来源
Java后台实现微信小程序不同人员生成不同小程序码并追踪扫码来源 下面我将详细介绍如何使用Java后台实现这一功能. 一.整体架构设计 前端:微信小程序 后端:Java (Spring Boot) 数 ...