golang context ctx
Go ontext Note1
在最近的面试过程被问到context(上下文,ctx)相关的内容,由于理解不是很清晰导致自己被问住了,故做一个笔记类型的梳理。另外声明:以下内容包括个人理解,如有错误欢迎指正^_^
context参考连接:go官方context库文件,go_blog,B站视频1
介绍
context的出现是为方便goroutines之间传递请求信息,如请求到达需要request handler,而连接DB需要另一个goroutine以及可能存在的rpc goroutine。使用context能够携带请求的一些信息,如超时信息等。
context.go 源码
在go标准库的context接口包括以下内容
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key any) any
}
方法 | 简要描述 |
---|---|
Deadline | 该方法返回deadline 截止日期,未设置deadline 时ok==false |
Done | 只读管道,用于实现阻塞 |
Err | 用于记录ctx的错误原因 |
Value | 用来返回key对应的value,相当于ctx内部的map |
当deadline
被调用时会关闭管道,并将为什么关闭记录在Err里 注意:对于value的官方建议用法:Use context values only for request-scoped data that transits processes and API boundaries, not for passing optional parameters to functions.
参考下图中多个功能执行过程,以context作为函数之间的消息传递。 图源自BiliBili
而在context初始化方面,需要借助空的context,在context包中有两种方法获取空的context,如下:
var (
background = new(emptyCtx)//不可导出
todo = new(emptyCtx)
)
func Background() Context {//可导出
return background
}
func TODO() Context {
return todo
}
实际上,Background()和TODO()都是构建一个空的context,只是语义上略有区别(更详细的可以看context源码,其中的注释说明B方法更适合于main中,而TODO则偏向于测试环境?个人理解)
常用方法
在context中有以下常用方法,包括context.WithValue
,context.WithCancel
,context.WithTimeout
等
example
context.WithValue
func main() {
grandp := context.Background()//empty context
father := tct1(grandp)//调用context.WithValue进行赋值,相当于继承自grandp
fmt.Printf("key1 %s", father.Value("key1"))
}
func tct1(ctx context.Context) context.Context {
//以hischild将接受context并给value赋值
hischild := context.WithValue(ctx, "key1", "value1")
return hischild
}
//输出 :key1 value1
context.WithCancel
func main() {
ctx, cancel := context.WithCancel(context.Background())
go func() {
time.Sleep(100 * time.Millisecond)
cancel() //调用cancel,ctx.Done()通道关闭
}()
select {
case <-time.After(300 * time.Millisecond):
fmt.Println("未超时")
case <-ctx.Done(): //ctx.Done()是一个管道,调用了cancel()都会关闭这个管道,然后读操作就会立即返回
err := ctx.Err() //如果发生Done(管道被关闭),Err返回Done的原因,可能是被Cancel了,也可能是超时了
fmt.Println("超时:", err) //context canceled
}
}
//输出:超时: context canceled
由于在100ms处就执行了cancel()
,所以会率先选择ctx.Done
通道,而time.After返回的是一个<-chan Time
只读管道,如果Sleep的时间长于300则会“未超时”。
context.WithTimeout
func main() {
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100) //超时后会自动调用context的Deadline,Deadline会,触发Done
defer cancel()
select {
case <-time.After(300 * time.Millisecond):
fmt.Println("未超时")
case <-ctx.Done(): //ctx.Done()是一个管道,context超时或者调用了cancel()都会关闭这个管道,然后读操作就会立即返回
err := ctx.Err() //如果发生Done(管道被关闭),Err返回Done的原因,可能是被Cancel了,也可能是超时了
fmt.Println("超时:", err) //context deadline exceeded
}
}
//输出:超时: context deadline exceeded
实际上,context.WithTimeout只是在WithCancel上指定超时时间,这一应用在Http请求处理等耗时处理中应用广泛。
Timeout的继承问题
通过context.WithTimeout创建的Context,其寿命不会超过父Context的寿命。比如: A处ctxA还有10s过期,而2s后B处接受该ctxA并使用WithTime
设为100s后过期,而实际上8s后ctxB也会到期。
在Http中的应用
func hello(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
fmt.Println("server: hello handler started")
defer fmt.Println("server: hello handler ended")
//net/http为每个请求创建了一个 context.Context, 并且可以通过 Context() 方法获取并使用它。
select {
case <-time.After(10 * time.Second)://模拟计算耗时
fmt.Fprintf(w, "hello\n")
case <-ctx.Done():
err := ctx.Err()
fmt.Println("server:", err)
internalError := http.StatusInternalServerError
http.Error(w, err.Error(), internalError)
}
}
func main() {
http.HandleFunc("/hello", hello)
http.ListenAndServe(":8090", nil)
}
在两个终端启动:
go run context-in-http-servers.go #//TerminalA
curl 127.0.0.1:8090/hello #//TerminalB
------------------------------------------------
#输出:
server: hello handler started
#Ctrl c终止TerminalB,10s前!
server: context canceled
server: hello handler ended
#不中断TerminalB中curl
server: hello handler started
server: hello handler ended
#正常开始以及结束
这里在TerminalB中,模拟客户端发出 /hello 请求, 在服务端开始处理后,按下 Ctrl C 以发出取消信号,从而导致关闭通道输出context的err。
2023年8月6日20:30:47
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgajbhh
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01