面试题答案
一键面试- 使用
context.WithCancel
创建可取消的上下文: 在业务逻辑开始时,通过context.WithCancel
创建一个context.Context
和对应的取消函数cancel
。例如:
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
- 在资源操作函数中传递上下文:
对于每个涉及资源操作的函数(如数据库连接、文件句柄操作等),都将
context.Context
作为第一个参数传递。这样函数内部可以根据上下文状态提前结束操作。
func connectDB(ctx context.Context) (*sql.DB, error) {
// 这里创建数据库连接
// 可以在连接过程中检查ctx.Done() 提前结束连接操作
}
- 使用
select
语句监听上下文取消信号: 在业务逻辑的关键位置,尤其是涉及到长时间运行的操作时,使用select
语句监听ctx.Done()
通道。当该通道接收到信号时,意味着上下文被取消,此时可以执行资源释放操作。
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
db, err := connectDB(ctx)
if err != nil {
// 处理错误
}
defer db.Close()
file, err := os.Open("example.txt")
if err != nil {
// 处理错误
}
defer file.Close()
go func() {
// 模拟长时间运行的操作
for {
select {
case <-ctx.Done():
// 释放资源
return
default:
// 正常业务逻辑
}
}
}()
// 其他业务逻辑
}
- 资源释放的封装: 为了确保所有资源都能被正确释放,可以将资源释放逻辑封装成函数。例如:
func releaseResources(db *sql.DB, file *os.File) {
if db != nil {
db.Close()
}
if file != nil {
file.Close()
}
}
然后在ctx.Done()
被触发时调用该函数:
select {
case <-ctx.Done():
releaseResources(db, file)
return
default:
// 正常业务逻辑
}
- 使用
context.WithTimeout
设置超时: 如果需要对业务逻辑设置一个最大执行时间,可以使用context.WithTimeout
代替context.WithCancel
。这同样有助于避免资源长时间占用导致的泄漏。
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
通过以上设计,可以在Go语言中利用Context
机制确保在上下文取消时,所有相关资源都能被正确释放,同时对正常业务逻辑的性能影响较小。