当前位置: 首页 > news >正文

golang面经——context模块

面试题相关

1、context 结构是什么样的?

分析:

        关于context要清楚具体是什么,context其实是一个接口,提供了四种方法,而在官方go语言中对context接口提供了四种基本类型的实现,回答的时候,要答出接口以及几种实现结构。

回答:

一、go语言里的context实际上是一个接口,提供了四种方法:

type Context interface {Deadline() (deadline time.Time, ok bool)  // Deadline方法的第一个返回值表示还有多久到期,第二个返回值表示是否被超时时间控制;Done() <-chan struct{}                   // Done()返回一个 只读channel,当这个channel被关闭时,说明这个context被取消;Err() error                              // Err()返回一个错误,表示channel被关闭的原因,例如是被取消,还是超时关闭;Value(key interface{}) interface{}       // value方法返回指定key对应的value,这是context携带的值。
}

Deadline的两个返回值:

deadline

含义:表示这个 Context 应该被取消的绝对时间点
类型:time.Time 类型的时间值
作用:
当设置了截止时间时,返回具体的时间点
当没有设置截止时间时,返回 time.Time 的零值(0001-01-01 00:00:00 +0000 UTC)

ok bool - 是否设置了截止时间
含义:指示这个 Context 是否设置了截止时间
类型:布尔值
可能值:
true:Context 设置了截止时间(如通过 WithDeadline 或 WithTimeout 创建)
false:Context 没有设置截止时间(如通过 Background() 或 WithCancel 创建)

二、有emptyCtx、cancelCtx、timerCtx、valueCtx四种实现:

        A. emptyCtx:emptyCtx 虽然实现了context接口,但是不具备任何功能,因为实现很简单,基本都是直接返回空值。我们一般调用context.Background()和context.TODO()都是返回一个 *emptyctx的动态类型(通过静态类型context.Context传递)。

        B. cancelCtx:cancelCtx同时实现Context和canceler接口,通过取消函数cancelFunc实现退出通知。注意其退D出通知机制不但通知自己,同时也通知其children节点。我们一般调用context.WithCancel()就会返回一个*cancelCtx 和cancelFunc

        C. timerCtx:timerCtx是一个实现了Context接口的具体类型,其内部封装了cancelctx类型实例,同时也有个C.deadline变量,用来实现定时退出通知。

        我们一般调用context,WithTimeout()就会返回一个*timerCtx和cancelFunc,不仅可以定时通知,也可以调用cancelFunc进行通知。

调用context.WithDeadline()也可以,WithTimeout是多少秒后进行通知,WithDeadline是在某个时间点通知,本质上,WithTimeout会转而WithDeadline。

  1. valueCtx: valueCtx是一个实现了Context接口的具体类型,其内部封装了Context接口类型,同时也封装了-个k/v的存储变量,其是一个实现了数据传递

我们一般context.WithValue()来得到一个*valueCtx,valuectx可以继承它的parent valuectx中的{key,value}。

2、context 使用场景和用途?(基本必问)

分析:

        这个问题其实可以可以上一个问题的补充提问,在明确了context是什么之后,即context接口提供了哪些哪些方法,以及有哪些实践之后,看似联想出这些实现是为了解决什么问题,主要突出两点:上下文信息传递和协程的取消控制。

回答:

  1. context 主要用来在 goroutine 之间传递上下文信息,比如传递请求的trace id,以便于追踪全局唯一请求
  2. 另一个用处是可以用来做取消控制,通过取消信号和超时时间来控制子goroutine的退出,防止goroutine泄漏。包括:取消信号、超时时间、截止时间、k-v 等。

知识点总结

1、context的用法总结

        在 Go 语言中,context 是一个核心包,用于在 API 边界之间传递请求作用域的值、取消信号和超时控制。它特别适用于管理跨多个 goroutine 的请求生命周期。

Context 的核心用途:
取消传播:向所有相关 goroutine 广播取消信号
超时控制:设置请求的最大执行时间
值传递:在调用链中安全传递请求相关的数据
截止时间:设置操作的绝对截止时间点

type Context interface {Deadline() (deadline time.Time, ok bool)  // 获取截止时间Done() <-chan struct{}                   // 返回取消信号通道Err() error                              // 返回取消原因Value(key interface{}) interface{}       // 获取关联值
}

1.1 context创建的几种方式        

        在 Go 语言的 context 包中,有几种主要的 Context 创建方式,每种都代表不同的使用场景和控制机制。以下是各种创建方式及其含义的详细解释:

// 示例:用户取消下载
func downloadFile(ctx context.Context) {for {select {case <-ctx.Done():fmt.Println("下载取消")returndefault:// 继续下载...}}
}// 调用
ctx, cancel := context.WithCancel(context.Background())
go downloadFile(ctx)// 用户点击取消时调用
cancel()

// 示例:HTTP请求超时
func fetchData() {ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)defer cancel() // 确保资源释放req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)resp, err := http.DefaultClient.Do(req)if errors.Is(err, context.DeadlineExceeded) {fmt.Println("请求超时")}
}

// 示例:每日报告生成
func generateDailyReport() {// 设置今天23:59:59为截止时间deadline := time.Date(2025, 9, 24, 23, 59, 59, 0, time.Local)ctx, cancel := context.WithDeadline(context.Background(), deadline)defer cancel()// 生成报告...
}

// 定义安全的key类型
type userKey struct{}func authMiddleware(next http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {// 验证用户并获取用户信息user := authenticate(r) // 将用户信息存入Contextctx := context.WithValue(r.Context(), userKey{}, user)next.ServeHTTP(w, r.WithContext(ctx))})
}// 在业务逻辑中获取
func handleRequest(w http.ResponseWriter, r *http.Request) {user := r.Context().Value(userKey{}).(*User)fmt.Fprintf(w, "欢迎, %s", user.Name)
}

        (1)一个上下文的节点只能存放一组kv

        (2)使用以下方式可以读取ctx值内容:

debugMode, _ := ctx.Value(debugKey).(bool)
timeout, _ := ctx.Value(timeoutKey).(time.Duration)

1.2 context 父子之间的关系,为什么要有父子关系?

        在 Go 的 context 包中,父子关系是 context 体系的核心设计,理解这种层级关系对于编写健壮的并发程序至关重要。以下是 context 父子关系的详细解析:

1)取消信号传播(核心价值)
级联取消:当父 Context 被取消时,所有派生出的子 Context 会自动收到取消信号
示例场景:

func main() {parent, cancelParent := context.WithCancel(context.Background())child, _ := context.WithCancel(parent)// 取消父ContextcancelParent()// 子Context会立即收到信号<-child.Done() // 此通道会立即关闭fmt.Println("子Context已取消")
}

实际应用:HTTP 服务器中,当客户端断开连接时,自动取消所有关联的数据库查询和下游服务调用。

2) 超时/截止时间继承
子 Context 的截止时间不会超过父 Context 的设置
自动取最短时间:

parent, _ := context.WithTimeout(context.Background(), 5*time.Second)
child, _ := context.WithTimeout(parent, 3*time.Second) // 实际超时:3秒deadline, _ := child.Deadline()
fmt.Println(deadline) // 显示3秒后的时间

设计意义:防止子操作意外延长整体执行时间

3)值传递的隔离与继承
值继承:子 Context 继承父 Context 的所有键值对
隔离性:在子 Context 中添加新值不会影响父 Context
示例:

parent := context.WithValue(context.Background(), "key", "parentValue")
child := context.WithValue(parent, "childKey", "childValue")fmt.Println(parent.Value("key"))        // parentValue
fmt.Println(child.Value("key"))         // parentValue (继承)
fmt.Println(child.Value("childKey"))    // childValue
fmt.Println(parent.Value("childKey"))   // nil (隔离)

4)资源生命周期管理
自动清理:当父 Context 取消时,所有关联资源(goroutine、连接等)可通过 defer cancel() 统一释放。
防泄漏机制:

func process(ctx context.Context) {// 忘记此调用会导致goroutine泄漏!defer cancel() go func() {select {case <-ctx.Done(): // 依赖父子传播自动终止return}}()
}

一个较为全面的例子:

package mainimport ("context""fmt""math/rand""net/http""sync""time"
)// 模拟服务依赖
type UserService struct{}
type OrderService struct{}
type NotificationService struct{}// 模拟用户信息
type User struct {ID   intName stringRole string
}// 模拟订单信息
type Order struct {ID     intUserID intAmount float64Status string
}// 全局服务实例
var (userService       = &UserService{}orderService      = &OrderService{}notificationService = &NotificationService{}
)func main() {// 启动HTTP服务器http.HandleFunc("/batch-process", batchProcessHandler)http.HandleFunc("/user-orders", userOrdersHandler)fmt.Println("服务器启动在 :8080")http.ListenAndServe(":8080", nil)
}// 批量处理订单接口 - 展示复杂的父子Context使用
func batchProcessHandler(w http.ResponseWriter, r *http.Request) {// 父Context:设置整体超时和请求IDparentCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)defer cancel()// 添加请求标识和用户信息parentCtx = context.WithValue(parentCtx, "requestID", generateRequestID())parentCtx = context.WithValue(parentCtx, "user", User{ID: 123, Name: "admin", Role: "admin"})fmt.Printf("[%s] 开始批量处理订单\n", parentCtx.Value("requestID"))// 并发处理多个订单orderIDs := []int{1001, 1002, 1003, 1004, 1005}results := processOrdersConcurrently(parentCtx, orderIDs)// 汇总结果successCount := 0for _, result := range results {if result.Success {successCount++}}response := fmt.Sprintf("批量处理完成: 成功 %d/%d 个订单", successCount, len(orderIDs))fmt.Fprintln(w, response)fmt.Printf("[%s] %s\n", parentCtx.Value("requestID"), response)
}// 并发处理订单
func processOrdersConcurrently(ctx context.Context, orderIDs []int) []ProcessResult {var wg sync.WaitGroupresults := make(chan ProcessResult, len(orderIDs))// 为每个订单创建独立的子Contextfor _, orderID := range orderIDs {wg.Add(1)// 创建子Context,设置更严格的超时childCtx, cancel := context.WithTimeout(ctx, 3*time.Second)childCtx = context.WithValue(childCtx, "orderID", orderID)go func(ctx context.Context, cancel context.CancelFunc, orderID int) {defer wg.Done()defer cancel() // 确保子Context资源释放// 模拟处理单个订单result := processSingleOrder(ctx)results <- result}(childCtx, cancel, orderID)}// 等待所有订单处理完成go func() {wg.Wait()close(results)}()// 收集结果var finalResults []ProcessResultfor result := range results {finalResults = append(finalResults, result)}return finalResults
}// 处理单个订单
func processSingleOrder(ctx context.Context) ProcessResult {orderID := ctx.Value("orderID").(int)requestID := ctx.Value("requestID").(string)fmt.Printf("[%s] 开始处理订单 %d\n", requestID, orderID)// 步骤1: 验证订单 - 创建孙子ContextvalidateCtx, validateCancel := context.WithTimeout(ctx, 1*time.Second)defer validateCancel()if err := validateOrder(validateCtx, orderID); err != nil {return ProcessResult{OrderID: orderID,Success: false,Error:   fmt.Sprintf("验证失败: %v", err),}}// 检查父Context是否已取消select {case <-ctx.Done():return ProcessResult{OrderID: orderID,Success: false,Error:   fmt.Sprintf("处理被取消: %v", ctx.Err()),}default:}// 步骤2: 处理支付 - 创建孙子ContextpaymentCtx, paymentCancel := context.WithTimeout(ctx, 2*time.Second)defer paymentCancel()if err := processPayment(paymentCtx, orderID); err != nil {return ProcessResult{OrderID: orderID,Success: false,Error:   fmt.Sprintf("支付失败: %v", err),}}// 检查父Context是否已取消select {case <-ctx.Done():return ProcessResult{OrderID: orderID,Success: false,Error:   fmt.Sprintf("处理被取消: %v", ctx.Err()),}default:}// 步骤3: 发送通知 - 创建孙子ContextnotifyCtx, notifyCancel := context.WithTimeout(ctx, 1*time.Second)defer notifyCancel()if err := sendNotification(notifyCtx, orderID); err != nil {// 通知失败不影响整体成功fmt.Printf("[%s] 订单 %d 通知发送失败: %v\n", requestID, orderID, err)}fmt.Printf("[%s] 订单 %d 处理完成\n", requestID, orderID)return ProcessResult{OrderID: orderID,Success: true,Error:   "",}
}// 查询用户订单接口 - 展示更复杂的依赖关系
func userOrdersHandler(w http.ResponseWriter, r *http.Request) {// 父Context:设置整体超时parentCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()// 添加请求信息parentCtx = context.WithValue(parentCtx, "requestID", generateRequestID())parentCtx = context.WithValue(parentCtx, "userID", 456)fmt.Printf("[%s] 开始查询用户 %d 的订单\n", parentCtx.Value("requestID"), parentCtx.Value("userID"))// 创建多个并行查询任务var wg sync.WaitGroupresults := make(chan interface{}, 3)// 查询用户信息wg.Add(1)go func() {defer wg.Done()// 为用户查询创建子ContextuserCtx, userCancel := context.WithTimeout(parentCtx, 2*time.Second)defer userCancel()user, err := getUserInfo(userCtx)if err != nil {results <- fmt.Sprintf("用户查询失败: %v", err)} else {results <- user}}()// 查询订单列表wg.Add(1)go func() {defer wg.Done()// 为订单查询创建子ContextorderCtx, orderCancel := context.WithTimeout(parentCtx, 3*time.Second)defer orderCancel()orders, err := getUserOrders(orderCtx)if err != nil {results <- fmt.Sprintf("订单查询失败: %v", err)} else {results <- orders}}()// 查询统计信息wg.Add(1)go func() {defer wg.Done()// 为统计查询创建子ContextstatsCtx, statsCancel := context.WithTimeout(parentCtx, 2*time.Second)defer statsCancel()stats, err := getUserStats(statsCtx)if err != nil {results <- fmt.Sprintf("统计查询失败: %v", err)} else {results <- stats}}()// 等待所有查询完成go func() {wg.Wait()close(results)}()// 聚合结果response := make([]interface{}, 0)for result := range results {response = append(response, result)}fmt.Fprintf(w, "查询完成,返回 %d 个结果\n", len(response))fmt.Printf("[%s] 查询完成\n", parentCtx.Value("requestID"))
}// 模拟服务实现
func (us *UserService) GetUser(ctx context.Context, userID int) (*User, error) {select {case <-time.After(time.Duration(rand.Intn(500)) * time.Millisecond): // 模拟网络延迟if rand.Intn(10) == 0 { // 10%失败率return nil, fmt.Errorf("用户服务暂时不可用")}return &User{ID: userID, Name: fmt.Sprintf("User%d", userID), Role: "customer"}, ctx.Err()case <-ctx.Done():return nil, ctx.Err()}
}func (os *OrderService) GetOrders(ctx context.Context, userID int) ([]Order, error) {select {case <-time.After(time.Duration(rand.Intn(800)) * time.Millisecond): // 模拟网络延迟if rand.Intn(20) == 0 { // 5%失败率return nil, fmt.Errorf("订单服务暂时不可用")}return []Order{{ID: 2001, UserID: userID, Amount: 99.99, Status: "completed"},{ID: 2002, UserID: userID, Amount: 149.50, Status: "pending"},}, ctx.Err()case <-ctx.Done():return nil, ctx.Err()}
}func (ns *NotificationService) SendOrderConfirmation(ctx context.Context, orderID int) error {select {case <-time.After(time.Duration(rand.Intn(300)) * time.Millisecond): // 模拟网络延迟if rand.Intn(15) == 0 { // 约6.7%失败率return fmt.Errorf("通知服务暂时不可用")}return ctx.Err()case <-ctx.Done():return ctx.Err()}
}// 辅助函数
func validateOrder(ctx context.Context, orderID int) error {// 模拟验证过程select {case <-time.After(200 * time.Millisecond):if rand.Intn(20) == 0 { // 5%验证失败return fmt.Errorf("订单验证失败")}return nilcase <-ctx.Done():return ctx.Err()}
}func processPayment(ctx context.Context, orderID int) error {// 模拟支付过程select {case <-time.After(500 * time.Millisecond):if rand.Intn(10) == 0 { // 10%支付失败return fmt.Errorf("支付处理失败")}return nilcase <-ctx.Done():return ctx.Err()}
}func sendNotification(ctx context.Context, orderID int) error {// 模拟发送通知select {case <-time.After(100 * time.Millisecond):return notificationService.SendOrderConfirmation(ctx, orderID)case <-ctx.Done():return ctx.Err()}
}func getUserInfo(ctx context.Context) (*User, error) {userID := ctx.Value("userID").(int)return userService.GetUser(ctx, userID)
}func getUserOrders(ctx context.Context) ([]Order, error) {userID := ctx.Value("userID").(int)return orderService.GetOrders(ctx, userID)
}func getUserStats(ctx context.Context) (map[string]interface{}, error) {select {case <-time.After(300 * time.Millisecond):return map[string]interface{}{"total_orders": 15,"total_spent":  1250.75,}, nilcase <-ctx.Done():return nil, ctx.Err()}
}func generateRequestID() string {return fmt.Sprintf("REQ-%d", time.Now().UnixNano()%100000)
}// 处理结果结构
type ProcessResult struct {OrderID intSuccess boolError   string
}

http://www.dtcms.com/a/407257.html

相关文章:

  • Go基础:Go语言能用到的常用时间处理
  • ppt网站超链接怎么做网站报备流程
  • HTTP安全响应头--CSP(Content-Security-Policy)
  • 建筑网站排行榜网站界面设计专利
  • 如何开始第一个开源项目?
  • Moviechat论文阅读
  • 做电影网站算侵权吗上海建设安检站网站
  • 品牌网站模板wordpress判断是否登录
  • 门户网站建设主要内容为什么建设营销型网站
  • 双语版网站引导页常德尚一网
  • Day70 基本情报技术者 单词表05 数据结构
  • 百色高端网站建设网站建设登记表
  • Redis - Hyperloglog类型
  • 配置 Oracle Linux 8 仓库为 yum 源
  • 移动网站建设优势滁州公司做网站
  • 用网站模板 侵权 做了修改seo优化提升排名
  • Golang语言基础篇008_接口详解
  • 广州网站设计总部找北京赛车网站开发
  • 做网站需要会哪些计算机语言大学生实训网站建设心得
  • 2025全新的软件测试面试八股文(含答案+文档)
  • 制作网站的步骤域名省住房和城乡建设厅官方网站
  • 做薪酬调查有哪些网站公司域名注册注意事项
  • Spring AI: 为Java开发者赋能的AI工程框架
  • 网站建设制作费 税前扣除吗网站怎么显示建设中
  • 台州专业做网站西安模板建站公司
  • 【项目】Celery:构建高可用分布式任务队列系统
  • 《道德经》第二章
  • 线性复杂度找回文串?Manacher马拉车----字符串算法
  • 品牌服装网站源码做一个网站需要多久
  • 网站描述怎样写微信静首页制作代码