Go 语言 context 都能做什么?

news2024/9/27 12:18:30

原文链接: Go 语言 context 都能做什么?

很多 Go 项目的源码,在读的过程中会发现一个很常见的参数 ctx,而且基本都是作为函数的第一个参数。

为什么要这么写呢?这个参数到底有什么用呢?带着这样的疑问,我研究了这个参数背后的故事。

开局一张图:

在这里插入图片描述

核心是 Context 接口:

// A Context carries a deadline, cancelation signal, and request-scoped values
// across API boundaries. Its methods are safe for simultaneous use by multiple
// goroutines.
type Context interface {
    // Done returns a channel that is closed when this Context is canceled
    // or times out.
    Done() <-chan struct{}

    // Err indicates why this context was canceled, after the Done channel
    // is closed.
    Err() error

    // Deadline returns the time when this Context will be canceled, if any.
    Deadline() (deadline time.Time, ok bool)

    // Value returns the value associated with key or nil if none.
    Value(key interface{}) interface{}
}

包含四个方法:

  • Done():返回一个 channel,当 times out 或者调用 cancel 方法时。
  • Err():返回一个错误,表示取消 ctx 的原因。
  • Deadline():返回截止时间和一个 bool 值。
  • Value():返回 key 对应的值。

有四个结构体实现了这个接口,分别是:emptyCtx, cancelCtx, timerCtxvalueCtx

其中 emptyCtx 是空类型,暴露了两个方法:

func Background() Context
func TODO() Context

一般情况下,会使用 Background() 作为根 ctx,然后在其基础上再派生出子 ctx。要是不确定使用哪个 ctx,就使用 TODO()

另外三个也分别暴露了对应的方法:

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context

遵循规则

在使用 Context 时,要遵循以下四点规则:

  1. 不要将 Context 放入结构体,而是应该作为第一个参数传入,命名为 ctx
  2. 即使函数允许,也不要传入 nil 的 Context。如果不知道用哪种 Context,可以使用 context.TODO()
  3. 使用 Context 的 Value 相关方法只应该用于在程序和接口中传递和请求相关的元数据,不要用它来传递一些可选的参数。
  4. 相同的 Context 可以传递给不同的 goroutine;Context 是并发安全的。

WithCancel

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

WithCancel 返回带有新 Done 通道的父级副本。当调用返回的 cancel 函数或关闭父上下文的 Done 通道时,返回的 ctxDone 通道将关闭。

取消此上下文会释放与其关联的资源,因此在此上下文中运行的操作完成后,代码应立即调用 cancel

举个例子:

这段代码演示了如何使用可取消上下文来防止 goroutine 泄漏。在函数结束时,由 gen 启动的 goroutine 将返回而不会泄漏。

package main

import (
    "context"
    "fmt"
)

func main() {
    // gen generates integers in a separate goroutine and
    // sends them to the returned channel.
    // The callers of gen need to cancel the context once
    // they are done consuming generated integers not to leak
    // the internal goroutine started by gen.
    gen := func(ctx context.Context) <-chan int {
        dst := make(chan int)
        n := 1
        go func() {
            for {
                select {
                case <-ctx.Done():
                    return // returning not to leak the goroutine
                case dst <- n:
                    n++
                }
            }
        }()
        return dst
    }

    ctx, cancel := context.WithCancel(context.Background())
    defer cancel() // cancel when we are finished consuming integers

    for n := range gen(ctx) {
        fmt.Println(n)
        if n == 5 {
            break
        }
    }
}

输出:

1
2
3
4
5

WithDeadline

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)

WithDeadline 返回父上下文的副本,并将截止日期调整为不晚于 d。如果父级的截止日期已经早于 d,则 WithDeadline(parent, d) 在语义上等同于 parent

当截止时间到期、调用返回的取消函数时或当父上下文的 Done 通道关闭时,返回的上下文的 Done 通道将关闭。

取消此上下文会释放与其关联的资源,因此在此上下文中运行的操作完成后,代码应立即调用取消。

举个例子:

这段代码传递具有截止时间的上下文,来告诉阻塞函数,它应该在到达截止时间时立刻退出。

package main

import (
    "context"
    "fmt"
    "time"
)

const shortDuration = 1 * time.Millisecond

func main() {
    d := time.Now().Add(shortDuration)
    ctx, cancel := context.WithDeadline(context.Background(), d)

    // Even though ctx will be expired, it is good practice to call its
    // cancellation function in any case. Failure to do so may keep the
    // context and its parent alive longer than necessary.
    defer cancel()

    select {
    case <-time.After(1 * time.Second):
        fmt.Println("overslept")
    case <-ctx.Done():
        fmt.Println(ctx.Err())
    }
}

输出:

context deadline exceeded

WithTimeout

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

WithTimeout 返回 WithDeadline(parent, time.Now().Add(timeout))

取消此上下文会释放与其关联的资源,因此在此上下文中运行的操作完成后,代码应立即调用取消。

举个例子:

这段代码传递带有超时的上下文,以告诉阻塞函数应在超时后退出。

package main

import (
    "context"
    "fmt"
    "time"
)

const shortDuration = 1 * time.Millisecond

func main() {
    // Pass a context with a timeout to tell a blocking function that it
    // should abandon its work after the timeout elapses.
    ctx, cancel := context.WithTimeout(context.Background(), shortDuration)
    defer cancel()

    select {
    case <-time.After(1 * time.Second):
        fmt.Println("overslept")
    case <-ctx.Done():
        fmt.Println(ctx.Err()) // prints "context deadline exceeded"
    }

}

输出:

context deadline exceeded

WithValue

func WithValue(parent Context, key, val any) Context

WithValue 返回父级的副本,其中与 key 关联的值为 val

其中键必须是可比较的,并且不应是字符串类型或任何其他内置类型,以避免使用上下文的包之间发生冲突。 WithValue 的用户应该定义自己的键类型。

为了避免分配给 interface{},上下文键通常具有具体的 struct{} 类型。或者,导出的上下文键变量的静态类型应该是指针或接口。

举个例子:

这段代码演示了如何将值传递到上下文以及如何检索它(如果存在)。

package main

import (
    "context"
    "fmt"
)

func main() {
    type favContextKey string

    f := func(ctx context.Context, k favContextKey) {
        if v := ctx.Value(k); v != nil {
            fmt.Println("found value:", v)
            return
        }
        fmt.Println("key not found:", k)
    }

    k := favContextKey("language")
    ctx := context.WithValue(context.Background(), k, "Go")

    f(ctx, k)
    f(ctx, favContextKey("color"))
}

输出:

found value: Go
key not found: color

本文的大部分内容,包括代码示例都是翻译自官方文档,代码都是经过验证可以执行的。如果有不是特别清晰的地方,可以直接去读官方文档。

以上就是本文的全部内容,如果觉得还不错的话欢迎点赞转发关注,感谢支持。


官方文档:

  • https://pkg.go.dev/context@go1.20.5

源码分析:

  • https://mritd.com/2021/06/27/golang-context-source-code/
  • https://www.qtmuniao.com/2020/07/12/go-context/
  • https://seekload.net/2021/11/28/go-context.html

推荐阅读:

  • Go 语言 map 如何顺序读取?
  • Go 语言 map 是并发安全的吗?
  • Go 语言切片是如何扩容的?
  • Go 语言数组和切片的区别
  • Go 语言 new 和 make 关键字的区别
  • 为什么 Go 不支持 []T 转换为 []interface
  • 为什么 Go 语言 struct 要使用 tags

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/709620.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

postgresql数据库登录代理解析(包含登录协议包解析)

文章目录 postgresql数据库登录代理解析&#xff08;包含登录协议包解析&#xff09;背景描述版本不同对应的账号密码加密目标解析方法相关代码位置断点关键位置及相关重要变量 登录通信流程&#xff08;SCRAM-SHA-256方式&#xff09;代码实现相关参考资料 postgresql数据库登…

Python count()函数详解

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 count 1、指定搜索位置2、参数为负数3、列表的coun…

以太网OSI参考模型(四)

目录 OSI模型 一、物理层 二、数据链路层 三、网络层 四、传输层 五、会话层 六、表示层 七、应用层 OSI模型 OSI七层模型&#xff0c;是国际标准化组织(ISO)和国际电报电话咨询委员会(CCITT)1984年联合制定的开放系统互联参考模型&#xff0c;为开放式互联信息系统提供…

06-C++学习笔记-指针的定义与使用

指针是C中非常重要的概念&#xff0c;它允许直接访问内存地址&#xff0c;并通过地址操作变量。本篇笔记将介绍指针的定义与使用方法&#xff0c;以及指针与数组的关系。 1️⃣ 指针的引入 指针的引入是为了解决需要直接访问内存地址的情况。通过指针&#xff0c;可以间接访问…

Android TextView 展示特殊字符高度变高问题解决

背景 #在我们的项目中&#xff0c;展示文字是很常见的需求&#xff0c;但是在线上展示中发现&#xff0c;有些信息是特殊字符展示的&#xff0c;而且这些字符的高度会导致TextView的高度变高&#xff08;与正常字符比&#xff09;。 效果如下&#xff1a; 很明显&#xff0…

森云+优控配置摄像头进行录像和拍照测试

森云和优控配置摄像头进行录像和拍照测试 现在其实已经基本上明确自己的环境了&#xff0c;就是在ubuntu下的pycharm和vscode&#xff0c;然后下载conda&#xff08;但是不要默认的base环境&#xff0c;只要conda create -n xxx 这样的基本功能就好了&#xff09; 显示opencv…

AI绘画StableDiffusion:云端在线版使用笔记分享(Kaggle版)

玩AI绘画&#xff08;SD&#xff09;&#xff0c;自己电脑配置不够&#xff1f;今天给大家介绍一下如何baipiao在线版AI绘画StableDiffusion。 Kaggle 是世界上最大的数据科学社区&#xff0c;拥有强大的工具和资源&#xff0c;可帮助您实现数据科学目标。&#xff08;每周可以…

【MySQL学习笔记】(六)MySQL基本查询

表的增删查改 1 create1.1 单行数据全列插入1.2 多行数据 指定列插入1.3 插入否则更新1.4 替换 2 retrieve2.1 select列2.1.1 全列查询2.2.2 指定列查询2.2.3 查询字段为表达式2.2.4 为查询结果指定别名2.2.5 结果去重 2.2 where 条件2.2.1 null的查询 2.3 结果排序2.4 筛选分…

如何搭建一个实时对话转录应用(类似zoom中的文本转录功能)并部署到 Heroku

文章目录 应用搭建Assembly AI文件夹结构前端搭建HTMLindex.js 后端搭建server.jspackage.json Heroku 部署创建账号创建Heroku应用上传仓库 应用搭建 应用基于 html javascript使用的第三方 API 是 Assembly AI 提供的 Assembly AI 要使用 real-time transcript 功能&…

ChatGPT:为教育创新提供五大机遇

随着智能技术的不断发展&#xff0c;ChatGPT在教育场景中的创新价值可能比我们能够意识到的还要多。比如它可以自动处理作业、在线答疑&#xff0c;可以辅助语言学习、实时沟通&#xff0c;甚至还可以用于评估诊断、科学研究。国内外关于利用ChatGPT实现教育创新的场景描绘已经…

【Matplotlib】多级雷达图绘制

一、实例1&#xff1a;个人能力画像雷达图 # -*- coding: utf-8 -*- """ Created on Sat Jul 1 20:52:54 2023author: zcq """import numpy as np import matplotlib.pyplot as plt import matplotlibmatplotlib.rcParams[font.family]SimHei …

灯夹三角架

2个卡箍1个无头螺丝拼1个灯夹 还需要1个三脚架固定 difference(){union(){translate([18,-7,0])cube([14,14,1]);translate([-7,18,40])cube([14,14,1]);translate([-7,-32,40])cube([14,14,1]);points [[7,-32,40], //0[18,-7,0], //1[18,7,0], //2[7,-18,40], //3[7,…

【C++面向对象】足球比赛数据统计系统(面向对象练习)

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、51CTO技术博主 &#x…

【IoT】硬件产品经理:从入门到精通(卫朋)

目录 介绍 出版过程 CSDN学院 下单方式 个人介绍 介绍 2023 年已经过去了一半。 作为阶段小目标&#xff0c;这本书也如期发布了。 先来看看封面&#xff0c;这里也要感谢编辑们的辛苦付出。 ​ 出版过程 出版本身其实是一件比较严肃的事情。 过去的一年多时间&#…

进程的基本概念解读

目录 什么是进程 PCB OS中用于管理控制的数据结构 进程控制块PCB的作用 进程控制块中的信息 前趋图 背景 作用 表示 示例 程序的顺序执行 程序顺序执行的特征 程序的并发执行 程序的并发执行的特征 例题解读 进程的三种基本状态 进程的三种状态之间的转换 进…

linux下RabbitMQ的使用

文章目录 linux下RabbitMQ的使用首先docker启动网页打开网址&#xff1a;用户名和密码登录创建exchanges:创建Queues增加Queues的Bind linux下RabbitMQ的使用 首先docker启动 su rootsudo docker run -d --hostname rabbitsvr --name rabbit -p 5672:5672 -p 15672:15672 -p …

Uniform的理解

Uniform&#xff1a;一种从Cpu的应用&#xff0c;向Gpu中的着色器发送数据的方式&#xff0c;它是全局的&#xff0c;可以被任意着色器程序在任意阶段访问。 若声明了一个Uniform却没用过&#xff0c;编译器会默认移除这个变量&#xff0c;导致编译出的版本并不包含它&#xff…

配置本地的application-dev.yml读取nacos上面的配置

我想配置本地的application-dev.yml读取nacos上面的配置&#xff0c;我应该在配置文件里怎么写&#xff1f;并且在nacos里怎么建立 在本地的application-dev.yml文件中&#xff0c;您可以使用Spring Cloud Nacos来读取Nacos上的配置。以下是在配置文件和Nacos中设置的步骤&…

servlet+JSP与SpringBoot+Vue项目交互——servlet访问Vue页面

问题 servletJSP与SpringBootVue项目交互——servlet访问Vue页面 详细问题 笔者前一段时间开发一个项目&#xff0c;使用的技术框架是servletJSP&#xff0c;现阶段开发的项目技术框架为SpringBootVue&#xff0c;笔者现在需要输入servletJSP的路由地址&#xff08;登录页面…

3DTiles Next研究

3DTiles Next是Cesium发布的下一代3DTiles规范。 元数据 在这一代规范中&#xff0c;更重视元数据&#xff08;metadata&#xff0c;如建筑物ID或者类型等&#xff09;&#xff0c;通过这些元数据可以对3dtiles进行样式调整或者过滤显示。 关于详细的元数据规范介绍看这里。 …