golang 开发 深入了解背景
context的历史
context包在Go 1.7版本正式加入Go标准库。在加入之前我们看看Go团队核心成员Sameer Ajmani在2014年发表的一篇关于context介绍博客,地址:https://go.dev/blog/context 下面是介绍的翻译。
在Go服务器中,每个传入的请求都在自己的goroutine中处理。请求处理程序经常启动额外的goroutine来访问后端,如数据库和RPC服务。处理请求的goroutine集合通常需要访问特定于请求的值,例如最终用户的身份、授权令牌和请求的截止日期。当一个请求被取消或超时时,所有处理该请求的goroutine都应该迅速退出,这样系统就可以回收它们正在使用的任何资源。
在Google,我们开发了一个 context 包,可以轻松地将请求范围内的值、取消信号和截止日期跨API边界传递给处理请求所涉及的所有goroutine。该软件包作为上下文公开提供。
可以看到介绍里面主要说的是,应用中怎么控制大量的goroutine退出释放资源、请求范围内怎么传值,也就是说,context的引入主要是为了解决这两个问题。
context的基本使用
context初始化的方法有五个,一个是main goroutine 初始化,一个主要是是用来传值的,经常使用的就三个。
未超时的时候,执行取消,依赖取消函数
func main() {
ctx := context.Background()
ctx = context.WithValue(ctx, "main", "main-value")
ctx1,cancel := context.WithDeadline(ctx, time.Now().Add(3*time.Second))
go cancelFunc(ctx1)
//取消函数
cancel()
time.Sleep(time.Duration(5*time.Second))
}
执行结果
main-param main-value
退出协程
超时的时候执行结果,超过三秒的超时时间,依赖超时时间
func main() {
ctx := context.Background()
ctx = context.WithValue(ctx, "main", "main-value")
ctx1,_ := context.WithDeadline(ctx, time.Now().Add(3*time.Second))
go cancelFunc(ctx1)
time.Sleep(time.Duration(5*time.Second))
}
执行结果
main-param main-value
1秒
1秒
1秒
退出协程
看注释和执行流程,基本能理解,main函数调用cancel就能取消正在执行的goroutine协程。WithDeadline和WithTimeout 除过可以通过调用取消函数,还可以通过设置超时时间点和时间段取消goroutine的执行。
context如何控制多个goroutine,如何传值给多个goroutine
看图示,root context控制着所有的goroutine,context1控制着 goroutine2 goroutine3 goroutine4 goroutine5 goroutine6 goroutine7,context2 控制着goroutine4 goroutine5,context3 控制着goroutine6 goroutine7。
使用图示的goroutine 编写测试代码
package main
import (
"context"
"fmt"
"time"
)
func func1(ctx context.Context) {
fmt.Println("func1-main-param", ctx.Value("main"))
ctx1 := context.WithValue(ctx, "func1-param", "func1-value")
go func2(ctx1)
select {
case <-ctx1.Done():
fmt.Println("goroutine 1 exit")
}
}
func func2(ctx context.Context) {
fmt.Println("func2-main-param", ctx.Value("main"))
fmt.Println("func2-func1-param", ctx.Value("func1-param"))
ctx2, _ := context.WithCancel(ctx)
go func4(ctx2)
go func5(ctx2)
select {
case <-ctx2.Done():
fmt.Println("goroutine 2 exit")
}
}
func func4(ctx context.Context) {
select {
case <-ctx.Done():
fmt.Println("goroutine 4 exit")
}
}
func func5(ctx context.Context) {
select {
case <-ctx.Done():
fmt.Println("goroutine 5 exit")
}
}
func main() {
ctx := context.Background()
ctx = context.WithValue(ctx, "main", "main-value")
ctx,cancel := context.WithCancel(ctx)
go func1(ctx)
cancel()
time.Sleep(time.Duration(5*time.Second))
}
运行结果
func1-main-param main-value
goroutine 1 exit
func2-main-param main-value
func2-func1-param func1-value
goroutine 2 exit
goroutine 5 exit
goroutine 4 exit
可以看到最终实现结果和场景,跟Sameer Ajmani在博客描述的是一样的,context的出现主要两个作用,控制 goroutine,goroutine之间传值。
推荐阅读
-
深入了解如何在 Golang 中选择 T 和 t。
-
深入了解 Golang 中的单元测试和性能测试
-
golang 开发 深入了解背景
-
深入了解 Webpack 热更新原则:提高开发效率的关键 - 简介:
-
Android 摄像头]开发之三:深入了解摄像头的 ISP(图像信号处理)必看文章
-
Java 类加载器的作用 - 简介:类加载器是 Java™ 中一个非常重要的概念。类加载器负责将 Java 类的字节码加载到 Java 虚拟机中。本文首先详细介绍了 Java 类加载器的基本概念,包括代理模型、加载类的具体过程和线程上下文类加载器等。然后介绍了如何开发自己的类加载器,最后介绍了类加载器在 Web 容器和 OSGi™ 中的应用。 类加载器是 Java 语言的一项创新,也是 Java 语言广受欢迎的重要原因之一。它允许将 Java 类动态加载到 Java 虚拟机中并执行。类加载器从 JDK 1.0 开始出现,最初是为了满足 Java Applets 的需求而开发的,Java Applets 需要从远程位置下载 Java 类文件并在浏览器中执行。现在,类加载器已广泛应用于网络容器和 OSGi。一般来说,Java 应用程序的开发人员不需要直接与类加载器交互;Java 虚拟机的默认行为足以应对大多数情况。但是,如果遇到需要与类加载器交互的情况,而您又不太了解类加载器的机制,就很容易花费大量时间调试异常,如 ClassNotFoundException 和 NoClassDefFoundError。本文将详细介绍 Java 的类加载器,帮助读者深入理解 Java 语言中的这一重要概念。下面先介绍一些基本概念。 类加载器的基本概念 顾名思义,类加载器用于将 Java 类加载到 Java 虚拟机中。一般来说,Java 虚拟机以如下方式使用 Java 类:Java 源程序(.java 文件)经 Java 编译器编译后转换为 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码并将其转换为 java.lang 实例。每个实例都用来表示一个 Java 类。通过该实例的 newInstance 方法创建该类的对象。实际情况可能更加复杂,例如,Java 字节代码可能是由工具动态生成或通过网络下载的。 基本上,所有类加载器都是 java.lang.ClassLoader 类的实例。下面将详细介绍这个 Java 类。 java.lang.ClassLoader 类简介 java.lang.ClassLoader 类的基本职责是根据给定类的名称为其查找或生成相应的字节码,然后根据这些字节码定义一个 Java 类,即 java.lang.Class 类的实例。除此之外,ClassLoader 还负责加载 Java 应用程序所需的资源,如图像文件和配置文件。不过,本文只讨论它加载类的功能。为了履行加载类的职责,ClassLoader 提供了许多方法,其中比较重要的方法如表 1 所示。下文将详细介绍这些方法。 表 1.与加载类相关的 ClassLoader 方法
-
Code | 腾讯小哥哥带你深入了解小游戏的架构设计和开发。
-
深入了解开发、测试、前期和后期四个主要环境
-
我的对话记录:深入了解敏捷开发的关键关注点
-
前端新手必看:了解前端开发的历史背景