面试题答案
一键面试Go语言中context包的设计目的
context包的设计目的主要是在多个goroutine构成的工作流中,传递截止时间、取消信号等上下文信息,以确保在程序结束或发生特定情况时,所有相关的goroutine能被安全、高效地取消或结束,同时还能在goroutine之间传递请求范围的数据。
核心接口及其作用
- Context接口
- 是所有上下文的基接口,定义了四个方法:
Deadline
方法返回当前上下文的截止时间,ok
为true
表示设置了截止时间,当超过这个时间,上下文应该被取消。Done
方法返回一个通道,当上下文被取消或者超时时,该通道会被关闭。用于通知相关的goroutine停止工作。Err
方法返回上下文被取消的原因,如果Done
通道未关闭,返回nil
;如果Done
通道已关闭,根据不同的取消原因返回相应的错误(如context.Canceled
表示被手动取消,context.DeadlineExceeded
表示超时)。Value
方法用于从上下文中获取键值对数据,主要用于在goroutine之间传递请求范围的数据。
- 是所有上下文的基接口,定义了四个方法:
- CancelFunc类型
- 是一个函数类型,调用该函数可以取消对应的上下文。通常通过
context.WithCancel
、context.WithTimeout
或context.WithDeadline
函数创建上下文时返回。
- 是一个函数类型,调用该函数可以取消对应的上下文。通常通过
在微服务中使用context实现相关功能及代码示例
- 请求取消
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("Worker received cancel signal, exiting...")
return
default:
fmt.Println("Worker is working...")
time.Sleep(1 * time.Second)
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
go worker(ctx)
time.Sleep(3 * time.Second)
cancel()
time.Sleep(1 * time.Second)
}
- 超时控制
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("Worker received cancel signal (timeout), exiting...")
return
default:
fmt.Println("Worker is working...")
time.Sleep(1 * time.Second)
}
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
go worker(ctx)
time.Sleep(5 * time.Second)
}
- 传递请求范围的数据
package main
import (
"context"
"fmt"
)
func processRequest(ctx context.Context) {
value := ctx.Value("key")
if value != nil {
fmt.Printf("Received value from context: %v\n", value)
}
}
func main() {
ctx := context.WithValue(context.Background(), "key", "value")
processRequest(ctx)
}
在实际微服务开发中,对于嵌套的函数调用,可以将context
对象作为参数在函数间传递,从而在整个调用链中实现取消、超时控制以及数据传递。例如:
package main
import (
"context"
"fmt"
"time"
)
func innerFunction(ctx context.Context) {
select {
case <-ctx.Done():
fmt.Println("Inner function received cancel signal, exiting...")
return
default:
fmt.Println("Inner function is working...")
time.Sleep(1 * time.Second)
}
}
func outerFunction(ctx context.Context) {
innerFunction(ctx)
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
outerFunction(ctx)
}