Go标准库中的context包,提供了goroutine之间的传递信息的机制,信号同步,除此之外还有超时(timeout)和取消(cancel)机制。概括起来,Context可以控制子goroutine的运行,超时控制的方法调用,可以取消的方法调用。
context核心数据结构
- Context interface
type Context interface {
Deadline() (deadline time.Time, ok bool)
// 返回这个Context被取消的截止时间,如果没有设置截止时间,ok的值返回的是false
Done() <-chan struct{}
// 返回一个只读的channel 一般用来监听context的取消 当context关闭后,Done()返回一个被关闭的管道,关闭的管道仍然是可读的,据此goroutine可以收到关闭请求,当context还未关闭时,Done()返回nil
Err() error
// 当context关闭后,Err()返回context的关闭原因 当context还未关闭时,Err()返回nil
Value(key any) any
// 返回此cxt中指定key对应的value 用于goroutine间传递信息
}
- 生产对应的结构体
context包提供了4个方法创建不同类型的context
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) // CancelCtx
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) // DeadLineCtx
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) // TimeoutCtx
func WithValue(parent Context, key, val any) Context // ValueCtx
- emptyCtx
使用
1.WithCancel
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
go gOne(ctx, "任务一")
time.Sleep(time.Second * 10)
cancel()
time.Sleep(time.Second * 2)
fmt.Println("exit")
}
func gOne(ctx context.Context, name string) {
go gTwo(ctx, "任务二")
for {
select {
case <-ctx.Done():
fmt.Println(name + ":任务已停止")
return
case <-time.After(time.Second * 1):
fmt.Println(name + "任务执行中")
}
}
}
func gTwo(ctx context.Context, name string) {
for {
select {
case <-ctx.Done():
fmt.Println(name + ":任务已停止")
return
case <-time.After(time.Second * 2):
fmt.Println(name + "任务执行中")
}
}
}
2.WithDeadline WithTimeout
这两个方法本质是一样的 WithTimeout也是调用的WithDeadline方法
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
return WithDeadline(parent, time.Now().Add(timeout))
}
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, _ := context.WithTimeout(context.Background(), time.Second*5)
go gTimeOne(ctx, "任务一")
time.Sleep(10 * time.Second)
fmt.Println("exit")
}
func gTimeOne(ctx context.Context, name string) {
go gTimeTwo(ctx, "任务二")
for {
select {
case <-ctx.Done():
fmt.Println(name + ":任务已停止")
return
case <-time.After(time.Second * 1):
fmt.Println(name + "任务执行中")
}
}
}
func gTimeTwo(ctx context.Context, name string) {
for {
select {
case <-ctx.Done():
fmt.Println(name + ":任务已停止")
return
case <-time.After(time.Second * 2):
fmt.Println(name + "任务执行中")
}
}
}
3.WithValue
使用valueContext在协程之间共享数据
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx := context.WithValue(context.Background(), "param", "1234")
go gWork(ctx)
time.Sleep(time.Second * 5)
fmt.Println("exit")
}
func gWork(ctx context.Context) {
value := ctx.Value("param").(string)
fmt.Println(value)
}
由于valueCtx不具有Done() 相关方法的实现,一版是配合另几个方法使用
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
valueCtx := context.WithValue(ctx, "param", "1234")
go gWork2(valueCtx)
time.Sleep(time.Second * 5)
cancel()
time.Sleep(time.Second * 10)
fmt.Println("exit")
}
func gWork2(ctx context.Context) {
value := ctx.Value("param").(string)
fmt.Println(value)
for {
select {
case <-ctx.Done():
fmt.Println("任务已停止")
return
case <-time.After(time.Second * 2):
fmt.Println("任务执行中")
}
}
}
gin中context的使用
gin接口超时处理
1.利用context.WithTimeout的特性去实现
// 入侵式
https://gist.github.com/montanaflynn/ef9e7b9cd21b355cfe8332b4f20163c1
// 非入侵式,无须修改原来代码(可能有bug)
https://vearne.cc/archives/39130
2.官方提供的插件
https://github.com/gin-contrib/timeout