面试题答案
一键面试借助context包实现优雅关闭并发服务的方法
-
原理:
context
包提供了一种传递截止时间、取消信号和其他请求范围的值的机制,在整个goroutine
树中。- 通过一个
context
,可以向所有关联的goroutine
发送取消信号,从而实现优雅关闭。
-
主要实现步骤:
- 创建一个可取消的
context
,通常使用context.WithCancel
、context.WithTimeout
或context.WithDeadline
。 - 将这个
context
传递给需要取消的goroutine
。 - 在
goroutine
内部,定期检查context
的取消信号,例如通过ctx.Done()
通道是否关闭。 - 当收到取消信号时,执行清理操作,然后退出
goroutine
。
- 创建一个可取消的
示例代码
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"
)
func worker(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("收到取消信号,开始清理并退出")
// 在这里执行清理操作,例如关闭数据库连接等
return
default:
fmt.Println("工作中...")
time.Sleep(1 * time.Second)
}
}
}
func main() {
// 创建一个可取消的context
ctx, cancel := context.WithCancel(context.Background())
// 启动一个goroutine
go worker(ctx)
// 监听系统信号,如SIGINT(Ctrl+C)或SIGTERM
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
sig := <-sigs
fmt.Println()
fmt.Println(sig)
cancel() // 发送取消信号
}()
// 防止主函数退出
select {}
}
在上述代码中:
context.WithCancel
创建了一个可取消的context
。worker
函数在一个无限循环中工作,并通过select
语句监听ctx.Done()
通道,当该通道关闭时,执行清理操作并退出。- 在
main
函数中,通过signal.Notify
监听系统信号,当收到SIGINT
或SIGTERM
信号时,调用cancel
函数取消context
,从而通知worker
函数退出。