热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入解析Golang中Context的功能与应用

本文详细探讨了Golang中Context的核心功能及其应用场景,通过深入解析其工作机制,帮助读者更好地理解和运用这一重要特性,对于提升代码质量和项目开发效率具有重要的参考价值。

golang中context的作用详解

当一个goroutine可以启动其他goroutine,而这些goroutine可以启动其他goroutine,依此类推,则第一个goroutine应该能够向所有其它goroutine发送取消信号。

上下文包的唯一目的是在goroutine之间执行取消信号,而不管它们如何生成。上下文的接口定义为:

type Context interface {
 Deadline() (deadline time.Time, ok bool)
 Done() <- chan struct{}
 Err() error
 Value(key interface{}) interface{}
}
  • Deadline:第一个值是截止日期,此时上下文将自动触发“取消”操作。第二个值是布尔值,true表示设置了截止日期,false表示未设置截止时间。如果没有设置截止日期,则必须手动调用cancel函数来取消上下文。
  • Done:返回一个只读通道(仅在取消后),键入struct {},当该通道可读时,表示父上下文已经发起了取消请求,根据此信号,开发人员可以执行一些清除操作,退出goroutine
  • Err:返回取消上下文的原因
  • Value:返回绑定到上下文的值,它是一个键值对,因此您需要传递一个Key来获取相应的值,此值是线程安全的

要创建上下文,必须指定父上下文。两个内置上下文(背景和待办事项)用作顶级父上下文:

var (
 background = new(emptyCtx)
 todo = new(emptyCtx)
)
func Background() Context {
 return background
}
func TODO() Context {
 return todo
}

背景,主要ü在主函数,初始化和测试代码的sed,是树结构中,根上下文,这是不能被取消的顶层语境。TODO,当您不知道要使用什么上下文时,可以使用它。它们本质上都是emptyCtx类型,都是不可取消的,没有固定的期限,也没有为Context赋任何值:键入emptyCtx int

type emptyCtx int
func (_ *emptyCtx) Deadline() (deadline time.Time, ok bool) {
 return
}
func (_ *emptyCtx) Done() <- chan struct{} {
 return nil
}
func (_ *emptyCtx) Err() error {
 return nil
}
func (*emptyCtx) Value(key interface{}) interface{} {
 return nil
}

上下文包还具有几个常用功能:func WithCancel(父上下文)(ctx上下文,取消CancelFunc)func WithDeadline(父上下文,截止时间.Time)(上下文,CancelFunc)func WithTimeout(父上下文,超时时间。持续时间)(上下文,CancelFunc)func WithValue(父上下文,键,val接口{})上下文

请注意,这些方法意味着可以一次继承上下文以实现其他功能,例如,使用WithCancel函数传入根上下文,它会创建一个子上下文,该子上下文具有取消上下文的附加功能,然后使用此方法将context(context01)作为父上下文,并将其作为第一个参数传递给WithDeadline函数,与子context(context01)相比,获得子context(context02),它具有一个附加功能,可在之后自动取消上下文最后期限。

WithCancel

对于通道,尽管通道也可以通知许多嵌套的goroutine退出,但通道不是线程安全的,而上下文是线程安全的。

例如:

package main
import (
 "runtime"
 "fmt"
 "time"
 "context"
)
func monitor2(ch chan bool, index int) {
 for {
  select {
  case v := <- ch:
   fmt.Printf("monitor2: %v, the received channel value is: %v, ending
", index, v)
   return
  default:
   fmt.Printf("monitor2: %v in progress...
", index)
   time.Sleep(2 * time.Second)
  }
 }
}
func monitor1(ch chan bool, index int) {
 for {
  go monitor2(ch, index)
  select {
  case v := <- ch:
   // this branch is only reached when the ch channel is closed, or when data is sent(either true or false)
   fmt.Printf("monitor1: %v, the received channel value is: %v, ending
", index, v)
   return
  default:
   fmt.Printf("monitor1: %v in progress...
", index)
   time.Sleep(2 * time.Second)
  }
 }
}
func main() {
 var stopSingal chan bool = make(chan bool, 0)
 for i := 1; i <= 5; i = i + 1 {
  go monitor1(stopSingal, i)
 }
 time.Sleep(1 * time.Second)
 // close all gourtines
 cancel()
 // waiting 10 seconds, if the screen does not display , all goroutines have been shut down
 time.Sleep(10 * time.Second)
 println(runtime.NumGoroutine())
 println("main program exit!!!!")
}

执行的结果是:

monitor1: 5 in progress...
monitor2: 5 in progress...
monitor1: 2 in progress...
monitor2: 2 in progress...
monitor2: 1 in progress...
monitor1: 1 in progress...
monitor1: 4 in progress...
monitor1: 3 in progress...
monitor2: 4 in progress...
monitor2: 3 in progress...
monitor1: 4, the received channel value is: false, ending
monitor1: 3, the received channel value is: false, ending
monitor2: 2, the received channel value is: false, ending
monitor2: 1, the received channel value is: false, ending
monitor1: 1, the received channel value is: false, ending
monitor2: 5, the received channel value is: false, ending
monitor2: 3, the received channel value is: false, ending
monitor2: 3, the received channel value is: false, ending
monitor2: 4, the received channel value is: false, ending
monitor2: 5, the received channel value is: false, ending
monitor2: 1, the received channel value is: false, ending
monitor1: 5, the received channel value is: false, ending
monitor1: 2, the received channel value is: false, ending
monitor2: 2, the received channel value is: false, ending
monitor2: 4, the received channel value is: false, ending
1
main program exit!!!!

这里使用一个通道向所有goroutine发送结束通知,但是这里的情况相对简单,如果在一个复杂的项目中,假设多个goroutine有某种错误并重复执行,则可以重复关闭或关闭该通道通道,然后向其写入值,从而触发运行时恐慌。这就是为什么我们使用上下文来避免这些问题的原因,以WithCancel为例:

package main
import (
 "runtime"
 "fmt"
 "time"
 "context"
)
func monitor2(ctx context.Context, number int) {
 for {
  select {
  case v := <- ctx.Done():
   fmt.Printf("monitor: %v, the received channel value is: %v, ending
", number,v)
   return
  default:
   fmt.Printf("monitor: %v in progress...
", number)
   time.Sleep(2 * time.Second)
  }
 }
}
func monitor1(ctx context.Context, number int) {
 for {
  go monitor2(ctx, number)
  select {
  case v := <- ctx.Done():
   // this branch is only reached when the ch channel is closed, or when data is sent(either true or false)
   fmt.Printf("monitor: %v, the received channel value is: %v, ending
", number, v)
   return
  default:
   fmt.Printf("monitor: %v in progress...
", number)
   time.Sleep(2 * time.Second)
  }
 }
}
func main() {
 var ctx context.COntext= nil
 var cancel context.CancelFunc = nil
 ctx, cancel = context.WithCancel(context.Background())
 for i := 1; i <= 5; i = i + 1 {
  go monitor1(ctx, i)
 }
 time.Sleep(1 * time.Second)
 // close all gourtines
 cancel()
 // waiting 10 seconds, if the screen does not display , all goroutines have been shut down
 time.Sleep(10 * time.Second)
 println(runtime.NumGoroutine())
 println("main program exit!!!!")
}

WithTimeout和WithDeadline

WithTimeout和WithDeadline在用法和功能上基本相同,它们都表示上下文将在一定时间后自动取消,唯一的区别可以从函数的定义中看出,传递给WithDeadline的第二个参数是类型time.Duration类型,它是一个相对时间,表示取消超时后的时间。例:

package main
import (
 "runtime"
 "fmt"
 "time"
 "context"
)
func monitor2(ctx context.Context, index int) {
 for {
  select {
  case v := <- ctx.Done():
   fmt.Printf("monitor2: %v, the received channel value is: %v, ending
", index, v)
   return
  default:
   fmt.Printf("monitor2: %v in progress...
", index)
   time.Sleep(2 * time.Second)
  }
 }
}
func monitor1(ctx context.Context, index int) {
 for {
  go monitor2(ctx, index)
  select {
  case v := <- ctx.Done():
   // this branch is only reached when the ch channel is closed, or when data is sent(either true or false)
   fmt.Printf("monitor1: %v, the received channel value is: %v, ending
", index, v)
   return
  default:
   fmt.Printf("monitor1: %v in progress...
", index)
   time.Sleep(2 * time.Second)
  }
 }
}
func main() {
 var ctx01 context.COntext= nil
 var ctx02 context.COntext= nil
 var cancel context.CancelFunc = nil
 ctx01, cancel = context.WithCancel(context.Background())
 ctx02, cancel = context.WithDeadline(ctx01, time.Now().Add(1 * time.Second)) // If it"s WithTimeout, just change this line to "ctx02, cancel = context.WithTimeout(ctx01, 1 * time.Second)"
 defer cancel()
 for i := 1; i <= 5; i = i + 1 {
  go monitor1(ctx02, i)
 }
 time.Sleep(5 * time.Second)
 if ctx02.Err() != nil {
  fmt.Println("the cause of cancel is: ", ctx02.Err())
 }
 println(runtime.NumGoroutine())
 println("main program exit!!!!")
}

WithValue

一些必需的元数据也可以通过上下文传递,该上下文将附加到上下文中以供使用。元数据作为键值传递,但请注意,键必须具有可比性,并且值必须是线程安全的。

package main
import (
 "runtime"
 "fmt"
 "time"
 "context"
)
func monitor(ctx context.Context, index int) {
 for {
  select {
  case <- ctx.Done():
   // this branch is only reached when the ch channel is closed, or when data is sent(either true or false)
   fmt.Printf("monitor %v, end of monitoring. 
", index)
   return
  default:
   var value interface{} = ctx.Value("Nets")
   fmt.Printf("monitor %v, is monitoring %v
", index, value)
   time.Sleep(2 * time.Second)
  }
 }
}
func main() {
 var ctx01 context.COntext= nil
 var ctx02 context.COntext= nil
 var cancel context.CancelFunc = nil
 ctx01, cancel = context.WithCancel(context.Background())
 ctx02, cancel = context.WithTimeout(ctx01, 1 * time.Second)
 var ctx03 context.COntext= context.WithValue(ctx02, "Nets", "Champion") // key: "Nets", value: "Champion"

 defer cancel()
 for i := 1; i <= 5; i = i + 1 {
  go monitor(ctx03, i)
 }
 time.Sleep(5 * time.Second)
 if ctx02.Err() != nil {
  fmt.Println("the cause of cancel is: ", ctx02.Err())
 }
 println(runtime.NumGoroutine())
 println("main program exit!!!!")
}

关于上下文,还有一些注意事项:不要将Context存储在结构类型中,而是将Context明确传递给需要它的每个函数,并且Context应该是第一个参数。

即使函数允许,也不要传递nil Context,或者如果您不确定要使用哪个Context,请传递context。不要将可能作为函数参数传递给上下文值的变量传递。

到此这篇关于golang中context的作用的文章就介绍到这了,更多相关golang中context的作用内容请搜索编程笔记以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程笔记!


推荐阅读
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • 前言--页数多了以后需要指定到某一页(只做了功能,样式没有细调)html ... [详细]
  • Python 异步编程:深入理解 asyncio 库(上)
    本文介绍了 Python 3.4 版本引入的标准库 asyncio,该库为异步 IO 提供了强大的支持。我们将探讨为什么需要 asyncio,以及它如何简化并发编程的复杂性,并详细介绍其核心概念和使用方法。 ... [详细]
  • 本文详细介绍 Go+ 编程语言中的上下文处理机制,涵盖其基本概念、关键方法及应用场景。Go+ 是一门结合了 Go 的高效工程开发特性和 Python 数据科学功能的编程语言。 ... [详细]
  • PyCharm中配置Pylint静态代码分析工具
    本文详细介绍如何在PyCharm中配置和使用Pylint,帮助开发者进行静态代码检查,确保代码符合PEP8规范,提高代码质量。 ... [详细]
  • 本文介绍如何使用Objective-C结合dispatch库进行并发编程,以提高素数计数任务的效率。通过对比纯C代码与引入并发机制后的代码,展示dispatch库的强大功能。 ... [详细]
  • 本文详细介绍如何使用Python进行配置文件的读写操作,涵盖常见的配置文件格式(如INI、JSON、TOML和YAML),并提供具体的代码示例。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 深入理解 SQL 视图、存储过程与事务
    本文详细介绍了SQL中的视图、存储过程和事务的概念及应用。视图为用户提供了一种灵活的数据查询方式,存储过程则封装了复杂的SQL逻辑,而事务确保了数据库操作的完整性和一致性。 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
author-avatar
高淑美萱士建志
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有