2026-06-07 11:08:10 +08:00
|
|
|
|
---
|
2026-06-07 12:14:39 +08:00
|
|
|
|
tags: [go, golang, interview, context-questions]
|
|
|
|
|
|
create time: 2026-06-07 14:30
|
2026-06-07 11:08:10 +08:00
|
|
|
|
---
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
# Context 面试题 🌐
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
## 概述
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
本文件涵盖 Go Context 的 4 道高频面试题,涉及接口定义、作用机制、Value 查找和取消传播。Context 是 Go 1.7 引入的标准库接口,是并发编程中控制请求生命周期的核心工具。
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
## 关联笔记
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
- [[hzh/GolangStar/Go语言进阶/Context]] — Context 详细讲解
|
|
|
|
|
|
- [[hzh/GolangStar/Go语言原理/context原理]] — cancel chain / value chain 源码分析
|
|
|
|
|
|
- [[hzh/GolangStar/Go面试题库/代码面试题]] — Context + Channel 实战
|
|
|
|
|
|
|
|
|
|
|
|
## 正文
|
|
|
|
|
|
|
|
|
|
|
|
### Q1:Go 语言里的 Context 是什么? 🟡中等
|
|
|
|
|
|
|
|
|
|
|
|
> [!question] ❓ 思考一下
|
|
|
|
|
|
> 如果一个 HTTP 请求处理函数调用了 5 个下游服务,每个下游又调了更多服务,你怎么保证当请求超时或取消时所有调用链都能及时退出?
|
|
|
|
|
|
|
|
|
|
|
|
## 参考答案
|
|
|
|
|
|
|
|
|
|
|
|
Context 是一个**信号传递和范围控制的工具**,本质是一个接口:
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
|
type Context interface {
|
2026-06-07 12:14:39 +08:00
|
|
|
|
Deadline() (time.Time, bool) // 返回截止时间
|
|
|
|
|
|
Done() <-chan struct{} // 返回关闭 channel 作为取消信号
|
|
|
|
|
|
Err() error // 返回取消原因
|
|
|
|
|
|
Value(key interface{}) interface{} // 携带请求级键值对数据
|
2026-06-07 11:08:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
四个方法构成的协定:
|
|
|
|
|
|
|
|
|
|
|
|
| 方法 | 作用 |
|
|
|
|
|
|
|------|------|
|
|
|
|
|
|
| `Deadline()` | 告知任务何时应该被取消 |
|
|
|
|
|
|
| `Done()` | 关闭后通知所有监听者收到取消信号 |
|
|
|
|
|
|
| `Err()` | 解释取消原因(Canceled / DeadlineExceeded) |
|
|
|
|
|
|
| `Value()` | 在调用链中传递请求范围的键值对 |
|
|
|
|
|
|
|
|
|
|
|
|
> [!note] 📝 核心考点
|
|
|
|
|
|
> Context 的核心作用是三件事:**超时控制**、**取消信号传播**、**请求级数据传递**。面试时可以按这个顺序展开。
|
|
|
|
|
|
|
|
|
|
|
|
> [!warning] ⚠️ 高频陷阱
|
|
|
|
|
|
> Context 应该作为函数的**第一个参数**传递,**不要存储在结构体中**,传递的数据应该是**请求级别**的(如 request ID),不要滥用。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
### Q2:Context 有什么作用? 🟢简单
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
## 参考答案
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
三大核心作用:
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
| 作用 | 场景 | 示例 |
|
|
|
|
|
|
|------|------|------|
|
|
|
|
|
|
| **超时控制** | HTTP 请求调用多个下游服务 | `WithTimeout` |
|
|
|
|
|
|
| **取消信号传播** | 父请求取消时子操作自动退出 | `WithCancel` |
|
|
|
|
|
|
| **请求级数据传递** | 分布式链路追踪中的 request ID | `WithValue` |
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
```go
|
|
|
|
|
|
// 典型用法:HTTP handler 中
|
|
|
|
|
|
func handler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
|
|
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
|
|
|
|
// 将 ctx 传递给所有下游调用
|
|
|
|
|
|
result := fetchFromDownstream(ctx)
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
> [!tip] 💡 面试技巧
|
|
|
|
|
|
> "Context 就像一条传送带上的标签——它从请求入口开始,沿着调用链一路传递,任何环节都可以读取上面的信息,也可以随时发出'停止'信号让整条线停下来。"
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
---
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
### Q3:Context.Value 的查找过程是怎样的? 🟡中等
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
## 参考答案
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
**链式递归查找**,从当前 Context 沿父链向上直到找到 key 或到达根 Context。
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
```mermaid
|
|
|
|
|
|
graph TD
|
|
|
|
|
|
A[ctx.Value key] --> B{当前层有?}
|
|
|
|
|
|
B -->|是| C[返回 value]
|
|
|
|
|
|
B -->|否| D[parent.Value key]
|
|
|
|
|
|
D --> E{有?}
|
|
|
|
|
|
E -->|是| C
|
|
|
|
|
|
E -->|否| F[root.Value key]
|
|
|
|
|
|
F --> G{有?}
|
|
|
|
|
|
G -->|是| C
|
|
|
|
|
|
G -->|否| H[返回 nil]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
> [!info] 🔗 延伸阅读
|
|
|
|
|
|
> - [[hzh/GolangStar/Go语言原理/context原理]] — value chain 的实现细节
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
---
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
### Q4:Context 如何被取消? 🟡中等
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
## 参考答案
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
通过 **channel 关闭信号**实现,三种取消方式:
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
| 方式 | 创建函数 | 触发条件 |
|
|
|
|
|
|
|------|---------|---------|
|
|
|
|
|
|
| **主动取消** | `WithCancel` | 手动调用 cancel 函数 |
|
|
|
|
|
|
| **超时取消** | `WithTimeout` / `WithDeadline` | 到达指定时间自动 cancel |
|
|
|
|
|
|
| **级联取消** | 自动传播 | 父 Context 取消时子 Context 自动取消 |
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
```mermaid
|
|
|
|
|
|
graph LR
|
|
|
|
|
|
A[Root Context] --> B[WithCancel → child1]
|
|
|
|
|
|
A --> C[WithTimeout → child2]
|
|
|
|
|
|
B --> D[WithCancel → grandchild]
|
|
|
|
|
|
C --> E[WithCancel → grandchild2]
|
|
|
|
|
|
|
|
|
|
|
|
style A fill:#F44336,color:#fff
|
|
|
|
|
|
style B fill:#FF9800,color:#fff
|
|
|
|
|
|
style C fill:#2196F3,color:#fff
|
|
|
|
|
|
```
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
> [!tip] 💡 面试技巧
|
|
|
|
|
|
> 回答时可以强调:"Context 是一棵树的结构,父节点取消时,整棵子树都会取消。Done() 返回的 channel 会在任意一层被 cancel 时关闭。"
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
## 关联笔记
|
2026-06-07 11:08:10 +08:00
|
|
|
|
|
2026-06-07 12:14:39 +08:00
|
|
|
|
- [[hzh/GolangStar/Go语言进阶/Context]]
|
|
|
|
|
|
- [[hzh/GolangStar/Go语言原理/context原理]]
|
|
|
|
|
|
- [[hzh/GolangStar/Go面试题库/代码面试题]]
|