当前位置:首页 > 科技  > 软件

Go语言Context应用全攻略:异步编程利器

来源: 责编: 时间:2023-11-06 08:53:13 355观看
导读概述在 Go 语言中,Context(上下文)是一个非常重要的概念,特别是在处理请求时。允许在请求的整个生命周期内传递数据、控制请求的取消、处理超时等。本文将介绍 Go 语言中 Context 的使用,帮助更好地理解与处理请求的传递与

概述

在 Go 语言中,Context(上下文)是一个非常重要的概念,特别是在处理请求时。k3W28资讯网——每日最新资讯28at.com

允许在请求的整个生命周期内传递数据、控制请求的取消、处理超时等。k3W28资讯网——每日最新资讯28at.com

本文将介绍 Go 语言中 Context 的使用,帮助更好地理解与处理请求的传递与控制。k3W28资讯网——每日最新资讯28at.com

主要内容包括k3W28资讯网——每日最新资讯28at.com

Context 基础k3W28资讯网——每日最新资讯28at.com

Context 创建与传递k3W28资讯网——每日最新资讯28at.com

Context 的超时与取消k3W28资讯网——每日最新资讯28at.com

Context 的链式操作k3W28资讯网——每日最新资讯28at.com

Context 在并发中的应用k3W28资讯网——每日最新资讯28at.com

Context 的应用场景k3W28资讯网——每日最新资讯28at.com

最佳实践与注意事项k3W28资讯网——每日最新资讯28at.com

1. Context 基础

在 Go 语言中,context.Context 接口定义了一个请求的上下文。k3W28资讯网——每日最新资讯28at.com

它包含了请求的截止时间、取消信号和请求的数据。k3W28资讯网——每日最新资讯28at.com

使用 Context 可以在请求之间有效地传递数据,同时也可以控制请求的生命周期。k3W28资讯网——每日最新资讯28at.com

type Context interface {Deadline() (deadline time.Time, ok bool)Done() <-chan struct{}Err() errorValue(key interface{}) interface{}}

Context 接口包含了四个方法:Deadline() 返回 Context 的截止时间k3W28资讯网——每日最新资讯28at.com

Done() 返回一个通道,它会在 Context 被取消或超时时关闭k3W28资讯网——每日最新资讯28at.com

Err() 返回 Context 的错误信息,Value(key) 返回 Context 中与 key 关联的值。k3W28资讯网——每日最新资讯28at.com

2. Context 创建与传递

2.1 创建和传递 Context

package mainimport (  "context"  "fmt"  "time")func main() {  // 创建一个根Context  rootContext := context.Background()  // 创建一个带有超时时间的Context,这里设置超时时间为2秒  ctx, cancel := context.WithTimeout(rootContext, 2*time.Second)  defer cancel()  // 在新的goroutine中执行任务  go func(ctx context.Context) {    select {    case <-time.After(3 * time.Second):      fmt.Println("任务完成")    case <-ctx.Done():      fmt.Println("任务取消或超时")    }  }(ctx)    // 等待一段时间,模拟程序运行  time.Sleep(5 * time.Second)}

在这个例子中,创建了一个带有 2 秒超时时间的 Context,并在一个新的 goroutine 中执行一个任务。k3W28资讯网——每日最新资讯28at.com

在主 goroutine 中,等待了 5 秒,因此任务在超时之前完成,所以会输出"任务完成"。k3W28资讯网——每日最新资讯28at.com

2.2 使用 WithValue 传递数据

package mainimport (  "context"  "fmt")type key stringfunc main() {  // 创建一个根Context  rootContext := context.Background()  // 使用WithValue传递数据  ctx := context.WithValue(rootContext, key("userID"), 123)  // 在子函数中获取传递的数据  getUserID(ctx)}func getUserID(ctx context.Context) {  // 从Context中获取数据  if userID, ok := ctx.Value(key("userID")).(int); ok {    fmt.Println("UserID:", userID)  } else {    fmt.Println("UserID不存在")  }}

在这个示例中,使用 WithValue 方法在 Context 中传递了一个 userID 的值,并在 getUserID 函数中成功获取并打印了这个值。k3W28资讯网——每日最新资讯28at.com

3. Context 的超时与取消

3.1 设置请求超时时间

package mainimport (  "context"  "fmt"  "time")func main() {  // 创建一个根Context  rootContext := context.Background()  // 创建一个超时时间为2秒的Context  timeoutCtx, _ := context.WithTimeout(rootContext, 2*time.Second)  // 创建一个手动取消的Context  cancelCtx, cancel := context.WithCancel(rootContext)  defer cancel()  // 在新的goroutine中执行任务  go func(ctx context.Context) {    select {    case <-time.After(3 * time.Second):      fmt.Println("任务完成")    case <-ctx.Done():      fmt.Println("任务取消或超时")    }  }(timeoutCtx)  // 在另一个goroutine中执行任务  go func(ctx context.Context) {    select {    case <-time.After(1 * time.Second):      fmt.Println("另一个任务完成")    case <-ctx.Done():      fmt.Println("另一个任务取消")    }  }(cancelCtx)  // 等待一段时间,模拟程序运行  time.Sleep(5 * time.Second)}

在上面例子中,用 WithTimeout 方法创建了一个带有 2 秒超时时间的 Context。k3W28资讯网——每日最新资讯28at.com

在任务的 goroutine 中,用 select 语句监听了超时和 Context 的取消两个事件,以便及时响应。k3W28资讯网——每日最新资讯28at.com

3.2 处理请求取消

package mainimport (  "context"  "fmt"  "time")func main() {  // 创建一个根Context  rootContext := context.Background()  // 创建一个可以手动取消的Context  ctx, cancel := context.WithCancel(rootContext)  defer cancel()  // 在新的goroutine中执行任务  go func(ctx context.Context) {    select {    case <-time.After(3 * time.Second):      fmt.Println("任务完成")    case <-ctx.Done():      fmt.Println("任务取消")    }  }(ctx)  // 等待一段时间,手动取消任务  time.Sleep(2 * time.Second)  cancel()  // 等待一段时间,模拟程序运行  time.Sleep(1 * time.Second)}

在上面例子中,使用 WithCancel 方法创建了一个可以手动取消的 Context。k3W28资讯网——每日最新资讯28at.com

在主函数中,等待了 2 秒后,手动调用 cancel 函数取消了任务。k3W28资讯网——每日最新资讯28at.com

这时,在任务的 goroutine 中,ctx.Done() 会接收到取消信号,从而退出任务。k3W28资讯网——每日最新资讯28at.com

4. Context 的链式操作

在实际应用中,可能需要将多个 Context 串联起来使用。k3W28资讯网——每日最新资讯28at.com

Go 语言的 Context 提供了 WithCancel、WithDeadline、WithTimeout 等方法。k3W28资讯网——每日最新资讯28at.com

可以用这些方法实现多个 Context 的协同工作。k3W28资讯网——每日最新资讯28at.com

package mainimport (  "context"  "fmt"  "time")func main() {  // 创建一个根Context  rootContext := context.Background()  // 创建一个超时时间为2秒的Context  timeoutCtx, _ := context.WithTimeout(rootContext, 2*time.Second)  // 创建一个手动取消的Context  cancelCtx, cancel := context.WithCancel(rootContext)  defer cancel()  // 在新的goroutine中执行任务  go func(ctx context.Context) {    select {    case <-time.After(3 * time.Second):      fmt.Println("任务完成")    case <-ctx.Done():      fmt.Println("任务取消或超时")    }  }(timeoutCtx)  // 在另一个goroutine中执行任务  go func(ctx context.Context) {    select {    case <-time.After(1 * time.Second):      fmt.Println("另一个任务完成")    case <-ctx.Done():      fmt.Println("另一个任务取消")    }  }(cancelCtx)  // 等待一段时间,模拟程序运行  time.Sleep(5 * time.Second)}

在示例中,创建了一个带有 2 秒超时时间的 Context 和一个可以手动取消的 Context,然后分别传递给两个不同的任务。k3W28资讯网——每日最新资讯28at.com

在主函数中,等待了 5 秒,超时时间为 2 秒,因此第一个任务会因超时而取消,第二个任务则会在 1 秒后完成。k3W28资讯网——每日最新资讯28at.com

5. Context 在并发中的应用

5.1 使用 Context 控制多个协程

package mainimport (  "context"  "fmt"  "sync"  "time")func main() {  // 创建一个根Context  rootContext := context.Background()  // 创建一个可以手动取消的Context  ctx, cancel := context.WithCancel(rootContext)  defer cancel()  // 使用WaitGroup等待所有任务完成  var wg sync.WaitGroup  // 启动多个协程执行任务  for i := 0; i < 5; i++ {    wg.Add(1)    go func(id int) {      defer wg.Done()      select {      case <-time.After(time.Duration(id) * time.Second):        fmt.Println("任务", id, "完成")      case <-ctx.Done():        fmt.Println("任务", id, "取消")      }    }(i)  }  // 等待一段时间,然后手动取消任务  time.Sleep(2 * time.Second)  cancel()  // 等待所有任务完成  wg.Wait()}

在上面例子中,创建了一个可以手动取消的 Context,并使用 sync.WaitGroup 等待所有任务完成。k3W28资讯网——每日最新资讯28at.com

在 for 循环中,启动了 5 个协程,每个协程会等待一段时间后输出任务完成信息。k3W28资讯网——每日最新资讯28at.com

在主函数中,程序等待了 2 秒后,手动调用 cancel 函数取消了任务,协程会接收到取消信号并退出。k3W28资讯网——每日最新资讯28at.com

5.2 避免 Context 滥用

在使用 Context 时,要避免将 Context 放在结构体中。k3W28资讯网——每日最新资讯28at.com

因为 Context 应该作为函数参数传递,而不应该被放在结构体中进行传递。k3W28资讯网——每日最新资讯28at.com

Context 应该限定在程序的最小作用域,不要传递到不需要它的函数中。k3W28资讯网——每日最新资讯28at.com

6. Context 的应用场景

6.1 HTTP 请求中的 Context 使用

package mainimport (  "fmt"  "net/http"  "time")func handler(w http.ResponseWriter, r *http.Request) {  ctx := r.Context()  select {  case <-time.After(2 * time.Second):    fmt.Fprintln(w, "Hello, World!")  case <-ctx.Done():    err := ctx.Err()    fmt.Println("Server:", err)    http.Error(w, err.Error(), http.StatusInternalServerError)  }}func main() {  http.HandleFunc("/", handler)  http.ListenAndServe(":8080", nil)}

在上面示例中,创建了一个 HTTP 请求处理函数 handler。k3W28资讯网——每日最新资讯28at.com

在处理函数中,用 r.Context() 获取到请求的 Context,并在其中执行一个耗时的任务。k3W28资讯网——每日最新资讯28at.com

如果请求超时,ctx.Done() 会接收到取消信号,可以在其中处理请求超时的逻辑。k3W28资讯网——每日最新资讯28at.com

6.2 数据库操作中的 Context 使用

package mainimport (  "context"  "database/sql"  "fmt"  "time"  _ "github.com/go-sql-driver/mysql")func main() {  // 连接数据库  db, err := sql.Open("mysql", "username:password@tcp(localhost:3306)/database")  if err != nil {    fmt.Println("数据库连接失败:", err)    return  }  defer db.Close()  // 创建一个Context,设置超时时间为5秒  ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)  defer cancel()  // 在Context的超时时间内执行数据库查询  rows, err := db.QueryContext(ctx, "SELECT * FROM users")  if err != nil {    fmt.Println("数据库查询失败:", err)    return  }  defer rows.Close()  // 处理查询结果  for rows.Next() {    // 处理每一行数据  }}

在上面例子中,使用 database/sql 包进行数据库查询。创建了一个带有 5 秒超时时间的 Context,并在其中执行数据库查询。k3W28资讯网——每日最新资讯28at.com

如果查询时间超过 5 秒,Context 会接收到取消信号,可以在其中执行处理查询超时的逻辑。k3W28资讯网——每日最新资讯28at.com

6.3 其他业务场景中的 Context 使用

在其他业务场景中,可使用 Context 实现更多复杂的任务协同。k3W28资讯网——每日最新资讯28at.com

例如,使用 Context 在多个微服务之间进行数据传递和超时控制。k3W28资讯网——每日最新资讯28at.com

以下是一个示例,演示了如何在微服务架构中使用 Context 进行跨服务的数据传递k3W28资讯网——每日最新资讯28at.com

package mainimport (  "context"  "fmt"  "time")type Request struct {  ID int}type Response struct {  Message string}func microservice(ctx context.Context, reqCh chan Request, resCh chan Response) {  for {    select {    case <-ctx.Done():      fmt.Println("Microservice shutting down...")      return    case req := <-reqCh:      // 模拟处理请求的耗时操作      time.Sleep(2 * time.Second)      response := Response{Message: fmt.Sprintf("Processed request with ID %d", req.ID)}      resCh <- response    }  }}func main() {  // 创建根Context  rootContext := context.Background()  // 创建用于请求和响应的通道  reqCh := make(chan Request)  resCh := make(chan Response)  // 启动微服务  go microservice(rootContext, reqCh, resCh)  // 创建带有5秒超时时间的Context  ctx, cancel := context.WithTimeout(rootContext, 5*time.Second)  defer cancel()  // 发送请求到微服务  for i := 1; i <= 3; i++ {    req := Request{ID: i}    reqCh <- req    select {    case <-ctx.Done():      fmt.Println("Request timed out!")      return    case res := <-resCh:      fmt.Println(res.Message)    }  }}

在上面示例中,创建了一个简单的微服务模拟,它接收来自 reqCh 通道的请求,并将处理结果发送到 resCh 通道。k3W28资讯网——每日最新资讯28at.com

在主函数中,用带有 5 秒超时时间的 Context 来确保请求不会无限期等待,同时也能够处理超时的情况。k3W28资讯网——每日最新资讯28at.com

7. 最佳实践与注意事项

7.1 避免在函数库中使用 Context

通常情况下,应该在函数的参数列表中显式传递 Context,而不是将 Context 放在结构体中。k3W28资讯网——每日最新资讯28at.com

这样做可以使函数的行为更加明确,避免隐藏传递的 Context,提高代码的可读性和可维护性。k3W28资讯网——每日最新资讯28at.com

7.2 避免在结构体中嵌入 Context

尽管可以将 Context 作为结构体的成员嵌入,但这样的做法通常是不推荐的。k3W28资讯网——每日最新资讯28at.com

因为 Context 应该是在函数调用的时候传递,而不是嵌入在结构体中。k3W28资讯网——每日最新资讯28at.com

如果结构体的方法需要使用 Context,应该将 Context 作为参数传递给这些方法。k3W28资讯网——每日最新资讯28at.com

7.3 注意 Context 的传递路径

在实际应用中,要仔细考虑 Context 的传递路径。k3W28资讯网——每日最新资讯28at.com

若是在多个函数之间传递 Context,确保 Context 的传递路径清晰明了,避免出现歧义和混乱。k3W28资讯网——每日最新资讯28at.com

Context 的传递路径应该尽量短,不要跨越过多的函数调用。k3W28资讯网——每日最新资讯28at.com

总结

在 Go 语言中,Context 是一个强大的工具,用于处理请求的传递、控制和超时等。k3W28资讯网——每日最新资讯28at.com

通过合理地使用 Context,可以编写出更加稳定、高效的异步程序,提高系统的健壮性和可维护性。k3W28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-17162-0.htmlGo语言Context应用全攻略:异步编程利器

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Python编程必备:掌握列表遍历的六种神级技巧!

下一篇: 一年经验,你让我精通微服务开发,过分吗?

标签:
  • 热门焦点
  • K6:面向开发人员的现代负载测试工具

    K6 是一个开源负载测试工具,可以轻松编写、运行和分析性能测试。它建立在 Go 和 JavaScript 之上,它被设计为功能强大、可扩展且易于使用。k6 可用于测试各种应用程序,包括 Web
  • 不容错过的MSBuild技巧,必备用法详解和实践指南

    一、MSBuild简介MSBuild是一种基于XML的构建引擎,用于在.NET Framework和.NET Core应用程序中自动化构建过程。它是Visual Studio的构建引擎,可在命令行或其他构建工具中使用
  • Flowable工作流引擎的科普与实践

    一.引言当我们在日常工作和业务中需要进行各种审批流程时,可能会面临一系列技术和业务上的挑战。手动处理这些审批流程可能会导致开发成本的增加以及业务复杂度的上升。在这
  • 三万字盘点 Spring 九大核心基础功能

    大家好,我是三友~~今天来跟大家聊一聊Spring的9大核心基础功能。话不多说,先上目录:图片友情提示,本文过长,建议收藏,嘿嘿嘿!一、资源管理资源管理是Spring的一个核心的基础功能,不
  • 腾讯盖楼,字节拆墙

    来源 | 光子星球撰文 | 吴坤谚编辑 | 吴先之&ldquo;想重温暴刷深渊、30+技能搭配暴搓到爽的游戏体验吗?一起上晶核,即刻暴打!&rdquo;曾凭借直播腾讯旗下代理格斗游戏《DNF》一
  • ESG的面子与里子

    来源 | 光子星球撰文 | 吴坤谚编辑 | 吴先之三伏大幕拉起,各地高温预警不绝,但处于厄尔尼诺大&ldquo;烤&rdquo;之下的除了众生,还有各大企业发布的ESG报告。ESG是&ldquo;环境保
  • iQOO 11S评测:行业唯一的200W标准版旗舰

    【Techweb评测】去年底,iQOO推出了“电竞旗舰”iQOO 11系列,作为一款性能强机,该机不仅全球首发2K 144Hz E6全感屏,搭载了第二代骁龙8平台及144Hz电竞
  • OPPO K11搭载长寿版100W超级闪充:26分钟充满100%

    据此前官方宣布,OPPO将于7月25日也就是今天下午14:30举办新品发布会,届时全新的OPPO K11将正式与大家见面,将主打旗舰影像,和同档位竞品相比,其最大的卖
  • “买真退假” 这种“羊毛”不能薅

    □ 法治日报 记者 王春   □ 本报通讯员 胡佳丽  2020年初,还在上大学的小东加入了一个大学生兼职QQ群。群主&ldquo;七王&rdquo;在群里介绍一些刷单赚
Top