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

golang中context使用总结

一、context使用注意事项

在使用context时,有一些需要注意的事项,以及一些与性能优化相关的建议:

  1. 避免滥用context传递数据:context的主要目的是传递请求范围的数据和取消信号,而不是用于传递全局状态或大量数据。滥用context传递大量数据可能导致上下文对象变得臃肿,增加内存和GC压力。

  2. 不要修改已传递的context:传递的context是不可变的,即使在函数内部对其调用cancel方法也不会影响调用方的context。如果需要对context进行修改,应该通过返回一个新的派生context来实现。

  3. 只在需要时传递context:不要将context作为函数参数无限制地传递,而是在需要时传递。这样可以避免不必要的复杂性和代码膨胀。

  4. 及早检查取消信号:在使用context的地方,应该及早检查ctx.Done()的返回值,以尽早响应取消信号。在耗时操作前或可能阻塞的地方,应该通过select语句来监听多个操作,包括取消信号、超时和其他channel。

  5. 使用WithCancel替代WithTimeout:在可能的情况下,优先使用WithCancel函数来设置取消信号,而不是仅仅依赖于WithTimeout函数。这样可以有更精确的控制和更灵活的处理方式。

  6. 优化context的传递:在频繁调用的函数链中,避免在每个函数中重复传递相同的context,可以通过使用结构体或函数闭包将context作为参数进行传递,从而减少代码重复和提升性能。

  7. 及时取消不再需要的goroutine:如果在多个goroutine中使用context,确保在不再需要时及时取消goroutine,以避免资源浪费和潜在的goroutine泄漏。

这些注意事项和性能优化建议可帮助确保正确且高效地使用context,避免滥用和性能问题。根据具体场景和需求,可以灵活使用context的机制来优化代码的可读性、并发安全性和性能。

二、context使用举例

在这里插入图片描述

在Go语言中,context(上下文)是在不同goroutine之间传递请求范围数据、取消信号和超时处理的一种机制。下面详细介绍context的每种使用情况和相应的代码举例:

  1. 传递请求范围数据:

    package mainimport ("context""fmt"
    )// 定义一个键类型(key)用于context中的数据传递
    type key string// 在context中设置数据
    func withValue(ctx context.Context) {// 使用WithValue将数据存储在context中ctxWithData := context.WithValue(ctx, key("name"), "John")// 调用另一个函数,并将带有数据的context传递给它printName(ctxWithData)
    }// 从context中获取并使用数据
    func printName(ctx context.Context) {// 从context中获取数据,并进行类型断言if name, ok := ctx.Value(key("name")).(string); ok {fmt.Println("Name:", name)}
    }func main() {// 创建根contextctx := context.Background()// 传递context并设置数据withValue(ctx)
    }
    

    在上面的示例中,我们定义了一个key类型,用于在context中存储数据。然后,我们使用WithValue函数将数据存储在带有数据的context ctxWithData 中,并将其传递给printName函数。在printName函数中,我们使用Value方法从context中获取数据,并进行类型断言后打印出来。

  2. 取消信号:

    package mainimport ("context""fmt""time"
    )// 模拟一些耗时操作
    func performTask(ctx context.Context) {// 检查是否接收到取消信号select {case <-ctx.Done():fmt.Println("Task canceled")returndefault:// 模拟长时间运行的任务time.Sleep(5 * time.Second)fmt.Println("Task completed")}
    }func main() {// 创建根contextctx := context.Background()// 派生子context,并设置取消信号ctx, cancel := context.WithCancel(ctx)// 启动耗时操作的goroutine,并传递带有取消信号的contextgo performTask(ctx)// 模拟一些操作后取消任务time.Sleep(2 * time.Second)cancel() // 发送取消信号// 等待一段时间,确保程序有足够的时间处理取消信号time.Sleep(1 * time.Second)
    }
    

    在上面的示例中,我们创建了一个任务函数performTask,该函数会检查是否接收到取消信号。使用context.WithCancel函数创建派生的子context,并通过调用返回的cancel函数发送取消信号。然后,我们在一个goroutine中运行任务函数,并通过传递带有取消信号的context来监听取消信号。在主goroutine中,我们等待一段时间后调用cancel函数发送取消信号。当任务函数接收到取消信号后,它会打印"Task canceled"。

  3. 超时处理:

    package mainimport ("context""fmt""time"
    )// 模拟一些耗时操作
    func performTask(ctx context.Context) {// 检查是否接收到取消信号或超时select {case <-ctx.Done():fmt.Println("Task canceled")case <-time.After(5 * time.Second):fmt.Println("Task completed")}
    }func main() {// 创建根contextctx := context.Background()// 派生子context,并设置超时时间ctx, cancel := context.WithTimeout(ctx, 3*time.Second)defer cancel()// 启动耗时操作的goroutine,并传递带有超时设置的contextgo performTask(ctx)// 等待一段时间,确保程序有足够的时间处理超时或取消信号time.Sleep(5 * time.Second)
    }
    

    在上面的示例中,我们创建了一个任务函数performTask,该函数会检查是否接收到取消信号或超时。使用context.WithTimeout函数创建派生的子context,并通过调用返回的cancel函数来设置超时时间。然后,我们在一个goroutine中运行任务函数,并传递带有超时设置的context来监听超时或取消信号。在主goroutine中,我们等待一段时间以确保程序有足够的时间处理超时或取消信号。当超过超时时间后,任务函数会打印"Task canceled"。

这些是context在Go语言中的常见用法,它们使得在并发环境中处理请求范围数据、取消信号和超时变得更加简单和可靠。根据具体的使用场景,你可以选择适当的context函数来创建和传递context,并根据需要进行取消和超时处理。

相关文章:

  • 【自动化测试】基于Selenium + Python的web自动化框架!
  • SDL2 消息循环和事件响应
  • RESTful(Representational State Transfer)
  • 第五章 将对象映射到 XML - 指定映射 XML 文档的格式选项
  • 5、鸿蒙项目远程调试
  • 目标检测—YOLO系列(二 ) 全面解读论文与复现代码YOLOv1 PyTorch
  • 【数据结构】栈与队列的实现
  • Elasticsearch 索引库操作与 Rest API 使用详解
  • Cloud
  • 【解决】使用Element-Plus icon图标不显示
  • 云ES高级监控告警
  • 【机器学习】朴素贝叶斯算法:多项式、高斯、伯努利,实例应用(心脏病预测)
  • 跨境电商测评新方案,安全可靠,高成功率
  • Python开源项目GPEN——人脸重建(Face Restoration),模糊清晰、划痕修复及黑白上色的实践
  • 基于蝠鲼觅食算法优化概率神经网络PNN的分类预测 - 附代码
  • classpath对获取配置文件的影响
  • Django 博客开发教程 8 - 博客文章详情页
  • dva中组件的懒加载
  • flask接收请求并推入栈
  • PHP的类修饰符与访问修饰符
  • Ruby 2.x 源代码分析:扩展 概述
  • Spring核心 Bean的高级装配
  • 给第三方使用接口的 URL 签名实现
  • 函数式编程与面向对象编程[4]:Scala的类型关联Type Alias
  • 技术攻略】php设计模式(一):简介及创建型模式
  • 京东美团研发面经
  • 2017年360最后一道编程题
  • Unity3D - 异步加载游戏场景与异步加载游戏资源进度条 ...
  • 东超科技获得千万级Pre-A轮融资,投资方为中科创星 ...
  • #162 (Div. 2)
  • #LLM入门|Prompt#1.8_聊天机器人_Chatbot
  • (1)(1.9) MSP (version 4.2)
  • (4)事件处理——(7)简单事件(Simple events)
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (zt)最盛行的警世狂言(爆笑)
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (三) diretfbrc详解
  • (转)c++ std::pair 与 std::make
  • (转)清华学霸演讲稿:永远不要说你已经尽力了
  • (转载)hibernate缓存
  • . Flume面试题
  • .form文件_一篇文章学会文件上传
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .net core控制台应用程序初识
  • .Net IE10 _doPostBack 未定义
  • .net分布式压力测试工具(Beetle.DT)
  • .net通用权限框架B/S (三)--MODEL层(2)
  • .NET与java的MVC模式(2):struts2核心工作流程与原理
  • .NET中两种OCR方式对比
  • /usr/bin/python: can't decompress data; zlib not available 的异常处理
  • /var/lib/dpkg/lock 锁定问题
  • ??如何把JavaScript脚本中的参数传到java代码段中
  • [ 2222 ]http://e.eqxiu.com/s/wJMf15Ku
  • [ 蓝桥杯Web真题 ]-布局切换
  • [120_移动开发Android]008_android开发之Pull操作xml文件