当前位置:首页 > 文章列表 > Golang > Go教程 > 一文带你深入理解GolangContext包

一文带你深入理解GolangContext包

来源:脚本之家 2023-05-12 19:10:21 0浏览 收藏

知识点掌握了,还需要不断练习才能熟练运用。下面golang学习网给大家带来一个Golang开发实战,手把手教大家学习《一文带你深入理解GolangContext包》,在实现功能的过程中也带大家重新温习相关知识点,温故而知新,回头看看说不定又有不一样的感悟!

1. 基本原理

1.1 Context 包的介绍

在 Go 语言中,Context 包是用于传递请求范围数据、取消信号和截止时间的机制。它通常被用来处理 goroutine 之间的通信和取消。Context 包是 Go 语言内置的,它可以很方便地使用,而不需要额外的依赖。

Context 包是一个轻量级的工具,它提供了一个标准的接口,用于在 goroutine 之间传递请求范围的数据、取消信号和截止时间。Context 包实现了一种类似于树状结构的数据结构,其中每个节点都表示一个请求范围。每个节点都有一个唯一的 key-value 对,其中 key 是一个 interface{} 类型的值,而 value 则是任何类型的值。Context 包还提供了一个可选的超时机制,用于在一定时间后自动取消请求。

Context 包的核心是一个 Context 接口,它定义了一些方法,用于获取请求范围数据、取消请求和处理超时。

 type Context interface {
     Deadline() (deadline time.Time, ok bool)
     Done() 
  • Deadline() 方法返回截止时间和一个布尔值,指示截止时间是否已经设置。
  • Done() 方法返回一个只读的 channel,当请求被取消或超时时,该 channel 将被关闭。
  • Err() 方法返回一个错误,指示为什么请求被取消。
  • Value() 方法返回与给定key相关联的值,如果没有值,则返回 nil。

Context 包还提供了两个用于创建 Context 的函数:WithContext 和 Background。Background 函数返回一个空的 Context,而 WithContext 函数则根据给定的父 Context 创建一个新的 Context。

Context 包的基本原理是通过在 goroutine 之间传递 Context 来实现请求范围数据、取消信号和截止时间的管理。当一个 goroutine 创建了一个新的 goroutine 时,它将 Context 作为参数传递给新的 goroutine。新的goroutine 可以使用这个 Context 来访问请求范围数据、接收取消信号和处理超时。

1.2 Context 的创建

在 Golang 中,Context 可以通过 WithCancel、WithDeadline、WithTimeout 和 WithValue 等函数来创建。下面分别介绍这些函数的用法和注意事项。

1.2.1 WithCancel

WithCancel 函数可以用于创建一个 Context 对象,并返回一个可取消的上下文和一个取消函数。当调用取消函数时,会通知所有的 Context 对象和其子 Context 对象,使它们都取消执行。

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

下面是一个示例代码:

 package main
 ​
 import (
     "context"
     "fmt"
     "time"
 )
 ​
 func main() {
     parent := context.Background()
     ctx, cancel := context.WithCancel(parent)
     go func() {
         select {
         case 

在上面的代码中,我们首先使用 context.Background() 函数创建一个根 Context 对象 parent,然后使用 WithCancel 函数创建一个子 Context 对象 ctx,并返回一个可取消的上下文和一个取消函数 cancel。接下来,我们在一个 goroutine 中使用 select 语句监听 Context 对象的 Done 方法和 time.After 函数的返回值,如果 Done 方法返回一个非 nil 的 error,则说明 Context 已经被取消,否则说明 time.After 函数已经超时。在主函数中,我们调用 cancel 函数来通知 Context 对象和其子 Context 对象,使它们都取消执行。最后,我们使用 time.Sleep 函数让程序等待一段时间,以便观察 Context 的执行情况。

1.2.2 WithDeadline

WithDeadline 函数可以用于创建一个 Context 对象,并返回一个截止时间和一个取消函数。当超过截止时间时,会自动通知所有的 Context 对象和其子 Context 对象,使它们都取消执行。

 func WithDeadline(parent Context, deadline time.Time) (ctx Context, cancel CancelFunc)

下面是一个示例代码:

 package main
 ​
 import (
     "context"
     "fmt"
     "time"
 )
 ​
 func main() {
     parent := context.Background()
     ctx, cancel := context.WithDeadline(parent, time.Now().Add(5*time.Second))
     go func() {
         select {
         case 

在上面的代码中,我们首先使用 context.Background() 函数创建一个根 Context 对象 parent,然后使用 WithDeadline 函数创建一个子 Context 对象 ctx,并返回一个截止时间和一个取消函数 cancel。接下来,我们在一个 goroutine 中使用 select 语句监听 Context 对象的 Done 方法和 time.After 函数的返回值,如果 Done 方法返回一个非 nil 的 error,则说明 Context 已经被取消,否则说明 time.After 函数已经超时。在主函数中,我们调用 cancel 函数来通知 Context 对象和其子 Context 对象,使它们都取消执行。最后,我们使用 time.Sleep 函数让程序等待一段时间,以便观察 Context 的执行情况。

1.2.3 WithTimeout

WithTimeout 函数可以用于创建一个 Context 对象,并返回一个超时时间和一个取消函数。当超过超时时间时,会自动通知所有的 Context 对象和其子 Context 对象,使它们都取消执行。

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

下面是一个示例代码:

 package main
 ​
 import (
     "context"
     "fmt"
     "time"
 )
 ​
 func main() {
     parent := context.Background()
     ctx, cancel := context.WithTimeout(parent, 5*time.Second)
     go func() {
         select {
         case 

在上面的代码中,我们首先使用 context.Background() 函数创建一个根 Context 对象 parent,然后使用 WithTimeout 函数创建一个子 Context 对象 ctx,并返回一个超时时间和一个取消函数 cancel。接下来,我们在一个 goroutine 中使用 select 语句监听 Context 对象的 Done 方法和 time.After 函数的返回值,如果 Done 方法返回一个非 nil 的 error,则说明 Context 已经被取消,否则说明 time.After 函数已经超时。在主函数中,我们调用 cancel 函数来通知 Context 对象和其子 Context 对象,使它们都取消执行。最后,我们使用 time.Sleep 函数让程序等待一段时间,以便观察 Context 的执行情况。

1.2.4 WithValue

WithValue 函数可以用于创建一个 Context 对象,并返回一个包含指定值的 Context 对象。这个值可以是任意类型的数据,可以是基本类型、结构体或者指针等。需要注意的是,这个值只在当前 Context 对象及其子 Context 对象中有效,对于其他 Context 对象来说是不可见的。

 func WithValue(parent Context, key interface{}, val interface{}) Context

下面是一个示例代码:

 package main
 ​
 import (
     "context"
     "fmt"
 )
 ​
 type userKey struct{}
 ​
 func main() {
     parent := context.Background()
     ctx := context.WithValue(parent, userKey{}, "admin")
     go func() {
         if user, ok := ctx.Value(userKey{}).(string); ok {
             fmt.Printf("user is %s\n", user)
         } else {
             fmt.Println("user is not found")
         }
     }()
     select {}
 }

在上面的代码中,我们首先使用 context.Background() 函数创建一个根 Context 对象 parent,然后使用 WithValue 函数创建一个子 Context 对象 ctx,并返回一个包含指定值的 Context 对象。接下来,我们在一个 goroutine 中使用 ctx.Value 函数获取 Context 对象中的值,并判断其类型是否为字符串类型。如果是,则输出其值,否则输出 “user is not found”。在主函数中,我们使用select语句使程序一直运行,以便观察 Context 的执行情况。

2. Context 的使用场景

2.1 并发控制

一个很典型的使用场景是,当我们需要同时启动多个 goroutine 进行任务处理时,我们可以使用 Context 来控制这些 goroutine 的执行。在每个 goroutine 中,我们都可以检测 Context 对象是否被取消,如果是,则退出 goroutine 的执行,否则继续执行。

下面是一个示例代码:

 package main
 ​
 import (
     "context"
     "fmt"
     "sync"
 )
 ​
 func worker(ctx context.Context, wg *sync.WaitGroup) {
     defer wg.Done()
     for {
         select {
         default:
             fmt.Println("work")
         case 

在上面的代码中,我们首先使用 context.Background() 函数创建一个根 Context 对象 parent,然后使用 WithCancel 函数创建一个子 Context 对象 ctx,并返回一个取消函数 cancel。接下来,我们使用 sync.WaitGroup 来等待所有的 goroutine 执行完成。在主函数中,我们启动了三个 goroutine 来执行任务,同时使用 cancel 函数来通知这些 goroutine 取消执行。最后,我们使用Wait方法等待所有的 goroutine 执行完成。

2.2 超时控制

另一个典型的使用场景是,当我们需要对一个操作设置一个超时时间时,我们可以使用 Context 来控制这个操作的执行时间。在操作执行超时时,我们可以通知 Context 对象和其子 Context 对象取消执行。

下面是一个示例代码:

 package main
 ​
 import (
     "context"
     "fmt"
     "time"
 )
 ​
 func work(ctx context.Context) {
     for {
         select {
         default:
             fmt.Println("work")
         case 

在上面的代码中,我们首先使用 context.Background() 函数创建一个根 Context 对象 parent,然后使用 WithTimeout 函数创建一个子 Context 对象 ctx,并返回一个取消函数 cancel。在 work 函数中,我们启动一个无限循环,不断输出 “work”。同时,我们使用 select 语句来等待 Context 对象被取消。在主函数中,我们使用 defer 语句调用 cancel 函数,以确保 Context 对象被取消。由于我们在 WithTimeout 函数中设置了一个 5 秒的超时时间,因此当程序运行超过 5 秒时,work 函数就会停止执行。

2.3 数据库连接

在使用数据库连接时,我们通常需要保证连接池中的连接数量不会超过一定的阈值。如果连接池中的连接数量超过了阈值,则需要等待连接释放后再进行操作。在这种情况下,我们可以使用 Context 来控制连接的生命周期。

下面是一个示例代码:

 package main
 ​
 import (
     "context"
     "database/sql"
     "fmt"
     "sync"
     "time"
 ​
     _ "github.com/go-sql-driver/mysql"
 )
 ​
 const maxConn = 5
 ​
 func main() {
     db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/test")
     if err != nil {
         panic(err)
     }
     defer db.Close()
 ​
     ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
     defer cancel()
 ​
     connCh := make(chan *sql.Conn, maxConn)
     var wg sync.WaitGroup
     for i := 0; i 

在上面的代码中,我们首先使用 sql.Open 函数打开一个 MySQL 数据库的连接,并返回一个 DB 对象 db。接下来,我们使用 WithTimeout 函数创建一个 Context 对象 ctx,并设置一个超时时间为 5 秒。同时,我们创建一个容量为 maxConn 的 channel 对象 connCh,用于存储数据库连接。在g oroutine 中,我们使用 select 语句等待 Context 对象被取消。在每次循环中,我们检查连接池中连接的数量是否超过了阈值,如果没有,则使用 db.Conn 函数从连接池中获取一个新的连接,并将其存储到 connCh 中。最后,我们使用 sync.WaitGroup 等待所有的 goroutine 执行完成。

2.4 HTTP 请求

在使用 HTTP 请求时,我们通常需要设置一个超时时间,以确保请求能够在规定的时间内得到响应。在这种情况下,我们可以使用 Context 来控制HTTP请求的执行时间。

下面是一个示例代码:

 package main
 ​
 import (
     "context"
     "fmt"
     "io/ioutil"
     "net/http"
     "time"
 )
 ​
 func main() {
     client := http.DefaultClient
     ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
     defer cancel()
     req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://www.example.com", nil)
     if err != nil {
         fmt.Println(err)
         return
     }
 ​
     resp, err := client.Do(req)
     if err != nil {
         fmt.Println(err)
         return
     }
     defer resp.Body.Close()
 ​
     body, err := ioutil.ReadAll(resp.Body)
     if err != nil {
         fmt.Println(err)
         return
     }
 ​
     fmt.Println(string(body))
 }

在上面的代码中,我们首先使用 http.DefaultClient 创建一个 HTTP 客户端对象 client。接下来,我们使用 WithTimeout 函数创建一个 Context 对象 ctx,并设置一个超时时间为 5 秒。同时,我们使用 http.NewRequestWithContext 函数创建一个 HTTP 请求对象 req,并将 Context 对象 ctx 作为参数传递给该函数。在 Do 函数中,我们会自动将 Context 对象 ctx 传递给 HTTP 请求,并在超时时间到达后自动取消该请求。

2.5 gRPC 请求

在使用 gRPC 请求时,我们通常需要设置一个超时时间,以确保请求能够在规定的时间内得到响应。在这种情况下,我们可以使用 Context 来控制 gRPC 请求的执行时间。

下面是一个示例代码:

 package main
 ​
 import (
     "context"
     "fmt"
     "log"
     "time"
 ​
     pb "github.com/example/helloworld"
     "google.golang.org/grpc"
 )
 ​
 const (
     address     = "localhost:50051"
     defaultName = "world"
 )
 ​
 func main() {
     conn, err := grpc.Dial(address, grpc.WithInsecure())
     if err != nil {
         log.Fatalf("did not connect: %v", err)
     }
     defer conn.Close()
 ​
     c := pb.NewGreeterClient(conn)
 ​
     ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
     defer cancel()
 ​
     r, err := c.SayHello(ctx, &pb.HelloRequest{Name: defaultName})
     if err != nil {
         log.Fatalf("could not greet: %v", err)
     }
     log.Printf("Greeting: %s", r.GetMessage())
 }

在上面的代码中,我们首先使用 grpc.Dial 函数创建一个 gRPC 客户端连接对象 conn。接下来,我们使用 pb.NewGreeterClient 函数创建一个 GreeterClient 对象 c。然后,我们使用 WithTimeout 函数创建一个 Context 对象 ctx,并设置一个超时时间为 5 秒。最后,我们使用 GreeterClient 对象 c 的 SayHello 函数发送一个 gRPC 请求,并将 Context 对象 ctx 作为参数传递给该函数。在 SayHello 函数中,我们会自动将 Context 对象 ctx 传递给 gRPC 请求,并在超时时间到达后自动取消该请求。

3. 总结

通过本文的介绍,我们了解了 Golang 的 Context 包的基本原理、使用方法和示例代码。Context 包是 Go 语言中实现并发控制和超时控制的重要工具之一,可以帮助我们更加灵活地控制程序的执行。在实际应用中,我们可以使用 Context 包来实现一些复杂的功能,例如控制数据库连接池、处理 HTTP 请求和 gRPC 请求等。通过学习和使用 Context 包,我们可以更好地实现并发控制和超时控制,提高程序的可靠性和稳定性。

在使用 Context 包时,需要注意以下几点:

  • Context 对象是线程安全的,可以被多个 goroutine 同时访问。
  • 可以使用 WithCancel、WithDeadline 和 WithTimeout 函数来创建 Context 对象,并使用 context.Background 函数创建根 Context 对象。
  • 在进行 goroutine 间的通信时,可以使用 Context 对象来传递消息和控制 goroutine 的执行。
  • 在使用第三方库时,需要注意该库是否支持 Context 功能,以便正确地使用 Context 包。
  • 在使用 Context 包时,需要避免出现 Context 滥用的情况,应该根据实际需要仅仅传递必要的 Context 对象,避免将 Context 对象传递到太多的函数中,导致程序难以维护和调试。

通过本文的介绍,相信你已经对 Golang 的 Context 包有了更深入的了解和掌握。希望你可以在实际应用中灵活运用 Context 包,提高程序的效率和可靠性。

文中关于golang的知识介绍,希望对你的学习有所帮助!若是受益匪浅,那就动动鼠标收藏这篇《一文带你深入理解GolangContext包》文章吧,也可关注golang学习网公众号了解相关技术文章。

版本声明
本文转载于:脚本之家 如有侵犯,请联系study_golang@163.com删除
Redis缓冲区溢出及解决方案分享Redis缓冲区溢出及解决方案分享
上一篇
Redis缓冲区溢出及解决方案分享
Redis解决跨域存取Session问题
下一篇
Redis解决跨域存取Session问题
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    542次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    508次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    497次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    484次学习
查看更多
AI推荐
  • SEO标题魔匠AI:高质量学术写作平台,毕业论文生成与优化专家
    魔匠AI
    SEO摘要魔匠AI专注于高质量AI学术写作,已稳定运行6年。提供无限改稿、选题优化、大纲生成、多语言支持、真实参考文献、数据图表生成、查重降重等全流程服务,确保论文质量与隐私安全。适用于专科、本科、硕士学生及研究者,满足多语言学术需求。
    16次使用
  • PPTFake答辩PPT生成器:一键生成高效专业的答辩PPT
    PPTFake答辩PPT生成器
    PPTFake答辩PPT生成器,专为答辩准备设计,极致高效生成PPT与自述稿。智能解析内容,提供多样模板,数据可视化,贴心配套服务,灵活自主编辑,降低制作门槛,适用于各类答辩场景。
    30次使用
  • SEO标题Lovart AI:全球首个设计领域AI智能体,实现全链路设计自动化
    Lovart
    SEO摘要探索Lovart AI,这款专注于设计领域的AI智能体,通过多模态模型集成和智能任务拆解,实现全链路设计自动化。无论是品牌全案设计、广告与视频制作,还是文创内容创作,Lovart AI都能满足您的需求,提升设计效率,降低成本。
    32次使用
  • 美图AI抠图:行业领先的智能图像处理技术,3秒出图,精准无误
    美图AI抠图
    美图AI抠图,依托CVPR 2024竞赛亚军技术,提供顶尖的图像处理解决方案。适用于证件照、商品、毛发等多场景,支持批量处理,3秒出图,零PS基础也能轻松操作,满足个人与商业需求。
    37次使用
  • SEO标题PetGPT:智能桌面宠物程序,结合AI对话的个性化陪伴工具
    PetGPT
    SEO摘要PetGPT 是一款基于 Python 和 PyQt 开发的智能桌面宠物程序,集成了 OpenAI 的 GPT 模型,提供上下文感知对话和主动聊天功能。用户可高度自定义宠物的外观和行为,支持插件热更新和二次开发。适用于需要陪伴和效率辅助的办公族、学生及 AI 技术爱好者。
    37次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码