go的上下文(context)研究

简介: go的上下文(context)研究

引言

go的上下文官方说明有点难懂,可能是我太菜了,经过我自己的研究,总结了一下自己的想法

context上下文

顾名思义,context用于go代码传输上下文信息,例如在方法调用之间传递参数,传递栈信息等,另外可以通过context进行上下文控制.

它的最简单的使用方法为:

package main
import (
   "context"
   "fmt"
)
func main() {
   baseCtx := context.Background()
   stackArr := \[\]string{}
   stackArr = append(stackArr, "main")
   ctx := context.WithValue(baseCtx,"stack", stackArr)
   test(ctx)
}
func test(ctx context.Context)  {
   var stackArr \[\]string
   stackInterface := ctx.Value("stack")
   for _,val :=range stackInterface.(\[\]string){
      stackArr = append(stackArr,val)
   }
   fmt.Printf("test Call stack:%v \\n",stackArr)
   stackArr = append(stackArr,"test")
   ctx = context.WithValue(ctx,"stack",stackArr)
   test2(ctx)
}
func test2(ctx context.Context)  {
   stackInterface := ctx.Value("stack")
   fmt.Printf("test2 Call stack:%v \\n",stackInterface)
}

输出:

image.png

可以看到,在main中 声明了一个baseCtx,然后传递了一个stack的字符串数组,到test方法时附加了一个新的值,test方法额外覆盖了这个值,重新将值附加到了test2,

可以看出:上下文其中的一个作用就是在调用栈中传递参数

context声明获取

context的结构体都基于 emptyCtx 类型(int),在正常使用时我们不会接触到这个类型,而是使用封装之后的 background,todo:

var (
   background = new(emptyCtx)
   todo       = new(emptyCtx)
)

通过 Background(),Todo()方法进行获取一个上下文:

baseCtx := context.Background() 
   todoCtx := context.Todo()

要注意的是,

background和todo 返回的值没有任何区别,只是为了区分使用场景而使用了2个不同的方法和变量

background用于主函数初始化,测试中,在顶级调用栈时使用

而todo 用于不清楚要使用什么上下文的时候使用

context使用

上下文使用的步骤为:

1:声明一个基本context变量

2:通过context包的其他方法进行衍生赋值一个新的context,例如WithValue 方法

3:调用方法接收到context之后进行使用,例如Value方法

context的衍生方法

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

context的使用方法

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}
  • Deadline:返回绑定当前context的任务被取消的截止时间;如果没有设定期限,将返回ok == false。

  • Done: 当绑定当前context的任务被取消时,将返回一个关闭的channel;如果当前context不会被取消,将返回nil。

  • Err: 如果Done返回的channel没有关闭,将返回nil;如果Done返回的channel已经关闭,将返回非空的值表示任务结束的原因。如果是context被取消,Err将返回Canceled;如果是context超时,Err将返回DeadlineExceeded。

  • Value:返回context存储的键值对中当前key对应的值,如果没有对应的key,则返回nil。

withTimeout

该方法将返回一个带有超时时间的context,由下层接收该参数,通过监听done方法,如果超时时间到了,则会接收到超时消息(通过Done方法接收通道),如果上级主动调用Cancel方法,也会接收到消息

package main
import (
   "context"
   "fmt"
   "time"
)
func main() {
   baseCtx := context.Background()
   timeoutCtx, cancel := context.WithTimeout(baseCtx, time.Second*5)
   go test(timeoutCtx, "task 1")
   go test(timeoutCtx, "task 2")
   go test(timeoutCtx, "task 3")
   time.Sleep(time.Second * 10)
   cancel()
}
func test(ctx context.Context, taskName string) {
   for {
      select {
      case <-ctx.Done():
         fmt.Println(taskName, "received signal,monitor task exit,time=", time.Now().Unix())
         return
      default:
         deadline,ok:=ctx.Deadline()
         fmt.Println(taskName, "goroutine monitor,time=", time.Now().Unix(),"cancel time:",deadline.Unix(),ok)
         time.Sleep(1 * time.Second)
      }
   }
}

输出:

image.png

WithValue

该方法将返回一个带有key->value的context,传递到其他方法可通过Value获取值

package main
import (
   "context"
   "fmt"
)
func main() {
   baseCtx := context.Background()
   stackArr := \[\]string{}
   stackArr = append(stackArr, "main")
   ctx := context.WithValue(baseCtx,"stack", stackArr)
   test(ctx)
}
func test(ctx context.Context)  {
   var stackArr \[\]string
   stackInterface := ctx.Value("stack")
   for _,val :=range stackInterface.(\[\]string){
      stackArr = append(stackArr,val)
   }
   fmt.Printf("test Call stack:%v \\n",stackArr)
   stackArr = append(stackArr,"test")
   ctx = context.WithValue(ctx,"stack",stackArr)
   test2(ctx)
}
func test2(ctx context.Context)  {
   stackInterface := ctx.Value("stack")
   fmt.Printf("test2 Call stack:%v \\n",stackInterface)
}

输出:

image.png

WithDeadline

withDeadline作用跟WithTimeout类似,它需要传递一个最后运行时间,时间到了则超时:

timeoutCtx, cancel := context.WithDeadline(baseCtx, time.Now().Add(time.Duration(1)\*time.Second\*10))

WithCancel

withCancel的作用跟WithTimeout类似,但是取消了超时时间的控制,需要主动取消:

package main
import (
   "context"
   "fmt"
   "time"
)
func main() {
   baseCtx := context.Background()
   timeoutCtx, cancel := context.WithCancel(baseCtx)
   go test(timeoutCtx, "task 1")
   go test(timeoutCtx, "task 2")
   go test(timeoutCtx, "task 3")
   time.Sleep(time.Second * 3)
   cancel()
   time.Sleep(time.Second * 3)
}
func test(ctx context.Context, taskName string) {
   for {
      select {
      case <-ctx.Done():
         fmt.Println(taskName, "received signal,monitor task exit,time=", time.Now().Unix())
         return
      default:
         deadline,ok:=ctx.Deadline()
         fmt.Println(taskName, "goroutine monitor,time=", time.Now().Unix(),"cancel time:",deadline.Unix(),ok)
         time.Sleep(1 * time.Second)
      }
   }
}

输出:

image.png

context的作用

从上文可以看出,context最基本的作用为通过一个参数,连接协程/方法 栈的上下文,使其能够进行上下文通信

具体的应用场景有:

1:传递参数

2:超时控制,例如curl的时候控制超时时间

3:主动控制协程的退出

....

目录
相关文章
|
5月前
|
Go
go语言并发编程(五) ——Context
go语言并发编程(五) ——Context
|
29天前
|
存储 Go 数据库
Go语言Context包源码学习
【10月更文挑战第21天】Go 语言中的 `context` 包用于在函数调用链中传递请求上下文信息,支持请求的取消、超时和截止时间管理。其核心接口 `Context` 定义了 `Deadline`、`Done`、`Err` 和 `Value` 方法,分别用于处理截止时间、取消信号、错误信息和键值对数据。包内提供了 `emptyCtx`、`cancelCtx`、`timerCtx` 和 `valueCtx` 四种实现类型,满足不同场景需求。示例代码展示了如何使用带有超时功能的上下文进行任务管理和取消。
|
3月前
|
Go API
Go 利用上下文进行并发计算
Go 利用上下文进行并发计算
|
3月前
|
存储 SQL Go
一文弄懂Go语言的Context包,值得收藏!
在开发高效的Go应用程序时,处理超时、取消操作和传递请求范围的数据至关重要。Go标准库中的`context`包提供了一套强大的工具,用于在不同层级间传递取消信号、超时和截止时间等信息。本文首先概述了`context`包的核心功能,接着详细介绍了关键方法,如`WithCancel`、`WithDeadline`、`WithTimeout`和`WithValue`的使用场景。通过创建和派生上下文,开发者能更好地管理请求的生命周期,控制长时间运行的操作,并在并发环境中传递必要的数据。
53 1
|
4月前
|
JSON Java Serverless
函数计算产品使用问题之如何使用Go SDK从HTTP上下文中提取JSON数据
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
6月前
|
数据管理 Go 开发者
Golang深入浅出之-Go语言上下文(context)包:处理取消与超时
【4月更文挑战第25天】Go语言中的`context`包在并发、网络请求和长任务中至关重要,提供取消、截止时间和元数据管理。本文探讨`context`基础,如`Background()`、`TODO()`、`WithCancel()`、`WithDeadline()`和`WithTimeout()`。常见问题包括不当传递、过度使用`Background()`和`TODO()`以及忽略错误处理。通过取消和超时示例,强调正确传递上下文、处理取消错误和设置超时以提高应用健壮性和响应性。正确使用`context`是构建稳定高效Go应用的关键。
101 1
|
6月前
|
Go 开发者
Golang深入浅出之-Go语言上下文(context)包:处理取消与超时
【4月更文挑战第23天】Go语言的`context`包提供`Context`接口用于处理任务取消、超时和截止日期。通过传递`Context`对象,开发者能轻松实现复杂控制流。本文解析`context`包特性,讨论常见问题和解决方案,并给出代码示例。关键点包括:1) 确保将`Context`传递给所有相关任务;2) 根据需求选择适当的`Context`创建函数;3) 定期检查`Done()`通道以响应取消请求。正确使用`context`包能提升Go程序的控制流管理效率。
65 1
|
6月前
|
存储 安全 程序员
一文搞懂Go语言Context
一文搞懂Go语言Context
62 0
|
6月前
|
SQL Go 数据库
Go语言Context应用全攻略:异步编程利器
Go语言Context应用全攻略:异步编程利器
170 0
|
6月前
|
安全 Go 数据库
Go语言高级特性:Context深入解读
Go语言高级特性:Context深入解读
46 0