context上下文的意思, 可以用来做超时管理 ,context 可以设置截止日期,超时或调用取消函数来通知所有使用任何派生 context 的函数来停止运行并返回。
1.创建 context
(1) ctx := context.Background()
返回一个空 context。这只能用于高等级(在 main 或顶级请求处理中)用于派生。
(2) ctx := context.TODO() Context
返回也是空context,与Bg的区别在于静态分析工具可以使用它来验证 context 是否正确传递,这是一个重要的细节,因为静态分析工具可以帮助在早期发现潜在的错误,并且可以连接到 CI/CD 管道。
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)
2.使用context
(1) context.WithValue(parent Context, key, val interface{}) (ctx Context, cancel CancelFunc)
此函数接收 context 并返回派生 context,其中值 val 与 key 关联,并通过 context 树与 context 一起传递。这意味着一旦获得带有值的 context,从中派生的任何 context 都会获得此值。不建议使用 context 值传递关键参数,而是函数应接收签名中的那些值,使其显式化。
ctx := context.WithValue(context.Background(), key, "test")
(2) context.WithCancel(parent Context) (ctx Context, cancel CancelFunc)
返回派生 context 和取消函数。只有创建它的函数才能调用取消函数来取消此 context。如果您愿意,可以传递取消函数,但是,永远不要传递取消函数。
ctx, cancel := context.WithCancel(context.Background())
(3) context.WithDeadline(parent Context, d time.Time) (ctx Context, cancel CancelFunc)
此函数返回其父项的派生 context,当截止日期超过或取消函数被调用时,该 context 将被取消。例如,您可以创建一个将在以后的某个时间自动取消的 context,并在子函数中传递它。当因为截止日期耗尽而取消该 context 时,获此 context 的所有函数都会收到通知去停止运行并返回。
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(2 * time.Second))
(4) context.WithTimeout(parent Context, timeout time.Duration) (ctx Context, cancel CancelFunc)
此函数类似于 context.WithDeadline。不同之处在于它将持续时间作为参数输入而不是时间对象。此函数返回派生 context,如果调用取消函数或超出超时持续时间,则会取消该派生 context。
ctx, cancel := context.WithTimeout(context.Background(), 2 * time.Second)
Demo:
一个超时的例子:
package main
import (
"context"
"fmt"
"math/rand"
"time"
)
/* Context 可以用来做超时管理
*
*/
func sleepRandom(fromFunction string, ch chan int){
defer func(){fmt.Println(fromFunction, "sleepRandom complete")}()
seed := time.Now().UnixNano()
r := rand.New(rand.NewSource(seed))
randomNumber := r.Intn(100)
sleeptime := randomNumber + 100
fmt.Println(fromFunction, "starting sleep for", sleeptime, "ms")
time.Sleep(time.Duration(sleeptime) * time.Millisecond)
fmt.Println(fromFunction, "Waking up, slept for", sleeptime, "ms")
if ch != nil{
ch <- sleeptime
}
}
func sleepRandomContext(ctx context.Context, ch chan bool){
defer func(){
fmt.Println("sleepRandomContext complete")
ch <- true
}()
sleeptimeChan := make(chan int)
go sleepRandom("sleepRandomContext", sleeptimeChan)
select{
case <-ctx.Done():
fmt.Println("sleepRandomContext: Time to return")
case sleeptime := <- sleeptimeChan:
fmt.Println("sleep for ", sleeptime, "ms")
}
}
func doWorkContext(ctx context.Context){
ctxWithTimeout, cancelFunction := context.WithTimeout(ctx, time.Duration(100) * time.Millisecond)
fmt.Println("begin")
time.Sleep(1 * time.Second)
defer func(){
fmt.Println("doWorkContext complete")
cancelFunction()
}()
ch := make(chan bool)
go sleepRandomContext(ctxWithTimeout, ch)
select {
case <- ctx.Done():
fmt.Println("doWorkContext: Time to return")
case <- ch:
fmt.Println("sleepRandomContext returns")
}
}
func main(){
ctx := context.Background()
/*
* Context 可以用来做超时管理,当context关闭的时候,函数也跟着关闭了
*/
//cancelFunction 取消函数
//可以直接取消当前的context,context -> nil
ctxWithCancel, cancelFunction := context.WithCancel(ctx)
defer func(){
fmt.Println("Main Defer: canceling context")
cancelFunction()
}()
go func(){
time.Sleep(1 * time.Second)
//sleepRandom("Main", nil)
cancelFunction()
fmt.Println("Main Sleep complete. canceling context")
}()
doWorkContext(ctxWithCancel)
}