当前位置: 首页 > news >正文

如何确保异步任务在 HTTP 返回后继续执行?context.WithoutCancel

文章目录

  • 如何确保异步任务在 HTTP 返回后继续执行?
    • 问题
    • 分析
    • 如何确保异步任务在 HTTP 返回后继续执行?
      • (1)使用独立的 context
      • (2)手动传递父 ctx 中的值
      • (3)使用 context.WithoutCancel(Go 1.21+)
    • context.WithoutCancel
      • context.WithoutCancel 的优势
      • context.WithoutCancel 的注意事项

如何确保异步任务在 HTTP 返回后继续执行?

问题

在 Gin 框架中,如果直接在主请求处理函数中启动一个协程(go),当 HTTP 请求返回时,父 context.Context 可能会被取消,这会导致协程中的任务无法正常执行或提前终止。这是因为 Gin 的 context.Context 与 HTTP 请求的生命周期绑定,请求结束后,context 会被自动取消。

分析

在 Gin 中,c.Request.Context() 是与当前 HTTP 请求绑定的 context.Context。
当 HTTP 请求处理完成并返回响应时,Gin 会自动取消这个 context。

父 ctx 被取消的影响:
如果你在协程中使用父 ctx,而父 ctx 被取消,协程中的任务可能会收到取消信号,导致任务提前终止。
例如,如果你在协程中调用了 ctx.Done(),它可能会立即返回,导致任务无法完成。

如果你的协程没有监听 ctx.Done(),父 ctx 被取消不会直接导致协程任务终止。
但是,父 ctx 中存储的值(如 traceID、requestID 等)可能无法正常访问,因为父 ctx 被取消后,其内部状态可能被清理。

如何确保异步任务在 HTTP 返回后继续执行?

(1)使用独立的 context

在启动协程时,创建一个新的、独立的 context.Context,而不是直接使用 Gin 的 c.Request.Context()。
示例:

func handler(c *gin.Context) {
    // 创建一个新的、独立的 context
    asyncCtx := context.Background()

    // 启动协程,并传递新的 context
    go asyncTask(asyncCtx)
}

func asyncTask(ctx context.Context) {
    // 执行任务
    log.Info("Async task started")
    time.Sleep(5 * time.Second)
    log.Info("Async task completed")
}

缺点:
无法继承父 ctx 中的值(如 traceID、requestID 等)。

(2)手动传递父 ctx 中的值

在启动协程时,手动提取父 ctx 中的值(如 traceID、requestID 等),并将它们注入到新的 context 中。

func handler(c *gin.Context) {
    // 从父 ctx 中提取 traceID 和其他信息
    traceID := trace.SpanFromContext(c.Request.Context()).SpanContext().TraceID().String()
    requestID := c.GetString(string(log.RequestID))

    // 创建一个新的、独立的 context,并注入 traceID 和其他信息
    asyncCtx := context.Background()
    asyncCtx = context.WithValue(asyncCtx, "traceID", traceID)
    asyncCtx = context.WithValue(asyncCtx, log.RequestID, requestID)

缺点:
需要手动提取和注入值,代码稍显冗长。

(3)使用 context.WithoutCancel(Go 1.21+)

在 Go 1.21 及以上版本中,可以使用 context.WithoutCancel 创建一个不受父 ctx 取消影响的 context。

func handler(c *gin.Context) {
    // 使用 context.WithoutCancel 创建一个不受父 ctx 取消影响的 context
    asyncCtx := context.WithoutCancel(c.Request.Context())

    // 启动协程,并传递新的 context
    go asyncTask(asyncCtx)
}

func asyncTask(ctx context.Context) {
    // 执行任务
    log.Info("Async task started")
    time.Sleep(5 * time.Second)
    log.Info("Async task completed")
}

context.WithoutCancel

context.WithoutCancel(ctx) 是一个非常方便的工具,尤其是在 Go 1.21 及以上版本中。它可以直接创建一个不受父 context.Context 取消影响的子 context,同时继承父 ctx 中的所有值(如 traceID、requestID 等)。这样你既不需要手动提取和传递值,也不需要担心父 ctx 被取消后影响协程任务的执行

context.WithoutCancel(ctx) 是最方便的方式,尤其是在 Go 1.21 及以上版本中。它可以直接创建一个不受父 ctx 取消影响的 context,同时继承父 ctx 中的所有值,避免了手动提取和传递值的繁琐操作。

context.WithoutCancel 的优势

  • 继承父 ctx 中的所有值:
    context.WithoutCancel(ctx) 会继承父 ctx 中的所有值(如 traceID、requestID 等),无需手动提取和传递。
  • 不受父 ctx 取消的影响:
    即使父 ctx 被取消,context.WithoutCancel(ctx) 返回的 context 也不会被取消,协程任务可以继续执行。
  • 代码简洁:
    使用 context.WithoutCancel(ctx) 可以避免手动创建独立的 context 或提取和传递值的繁琐操作。

context.WithoutCancel 的注意事项

  • Go 版本要求:
    context.WithoutCancel 是 Go 1.21 引入的新功能,确保你的 Go 版本在 1.21 及以上。
  • 资源管理:
    即使父 ctx 被取消,context.WithoutCancel(ctx) 返回的 context 也不会被取消。因此,需要确保协程任务能够正常结束,避免资源泄漏
    值继承:
  • context.WithoutCancel(ctx) 会继承父 ctx 中的所有值,但不会继承父 ctx 的取消信号或超时设置。

文章转载自:
http://appaloosa.pzdurr.cn
http://chromonema.pzdurr.cn
http://amalgamable.pzdurr.cn
http://bbfc.pzdurr.cn
http://avionics.pzdurr.cn
http://bountifully.pzdurr.cn
http://anglepod.pzdurr.cn
http://autosemantic.pzdurr.cn
http://catoptrics.pzdurr.cn
http://bountiful.pzdurr.cn
http://angiosarcoma.pzdurr.cn
http://acantha.pzdurr.cn
http://ambary.pzdurr.cn
http://achromatopsy.pzdurr.cn
http://begird.pzdurr.cn
http://blackmailer.pzdurr.cn
http://biretta.pzdurr.cn
http://chinkapin.pzdurr.cn
http://asterixis.pzdurr.cn
http://bold.pzdurr.cn
http://beggarly.pzdurr.cn
http://awfully.pzdurr.cn
http://bearbaiting.pzdurr.cn
http://bleeper.pzdurr.cn
http://anamnestic.pzdurr.cn
http://bondslave.pzdurr.cn
http://androcentric.pzdurr.cn
http://accouter.pzdurr.cn
http://chaitya.pzdurr.cn
http://absorption.pzdurr.cn
http://www.dtcms.com/a/87266.html

相关文章:

  • 常见框架漏洞之五:中间件
  • 操作系统必知的面试题
  • 【STM32】初识STM32
  • Spring MVC请求与响应全解析:从参数绑定到异常处理
  • 蓝桥杯历届真题 填充#贪心算法
  • 什么是索引?为什么要使用B树作为索引数据结构?
  • C++11 标准库 `find` 与 `find_if` 详解
  • 基于Spring Boot的三国之家网站的设计与实现(LW+源码+讲解)
  • 学一个前端 UI 框架,要学些什么内容?
  • 使用ThreadLocal可能导致内存泄漏的原因与其底层实现机制
  • 干货!Kubernetes网络模型与访问管理
  • ctfshow REVERSE re2 萌新赛 内部赛 七夕杯 WP
  • 我的世界1.20.1forge模组进阶开发——生物生成2
  • 还在用Excel规划机房变更吗?
  • VSCode 出现一直Reactivating terminals,怎么破
  • ubuntu服务器server版安装,ssh远程连接xmanager管理,改ip网络连接。图文教程
  • “浅浅深究”一下ConcurrentHashMap
  • 12-scala样例类(Case Classes)
  • DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加导出数据功能示例14,TableView15_14多功能组合的导出表格示例
  • 使用 ByteDance 的 UI-TARS Desktop 探索 AI 驱动的 GUI 自动化新前沿
  • 1007 Maximum Subsequence Sum
  • 如何在IDEA中借助深度思考模型 QwQ 提高编码效率?
  • DeepSeek+RAG局域网部署
  • 微软纳德拉最新一期访谈
  • 如何删除git上最后一次提交,Git日常使用操作说明。
  • python高级4
  • Mysql从入门到精通day3————记一次连接查询的武装渗透
  • 【二分查找 树状数组 差分数组 离散化 】P6172 [USACO16FEB] Load Balancing P|省选-
  • 牛顿-拉夫逊迭代法原理与除法器的软件与硬件实现
  • 六十天Linux从0到项目搭建第四天(通配符命令、其他命令、压缩解压工具、shell的感性理解、linux权限解析)