面试题答案
一键面试Go context基本数据结构演变
早期Go语言在并发编程中对于资源管理、取消机制等处理相对分散。随着context
包引入,其基本数据结构Context
接口及相关实现类型不断发展。Context
接口定义了Deadline
、Done
、Err
和Value
等方法,不同实现类型(如emptyCtx
、cancelCtx
、timerCtx
、valueCtx
等)为不同场景提供支持。
对Go并发编程的影响
- 资源管理
- 影响:
context
通过Done
通道和CancelFunc
实现资源的及时释放。当父context
取消时,所有基于该context
创建的子context
及其关联的协程都能收到取消信号,从而可以清理相关资源。例如在HTTP服务器处理请求时,当客户端断开连接,对应的context
取消,处理该请求的协程可以停止不必要的数据库查询等操作,释放数据库连接等资源。 - 示例
- 影响:
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("worker: context cancelled, cleaning up resources")
return
default:
fmt.Println("worker: working...")
time.Sleep(100 * time.Millisecond)
}
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond)
defer cancel()
go worker(ctx)
time.Sleep(500 * time.Millisecond)
}
- 取消机制
- 影响:
context
提供了统一且方便的取消机制。可以通过context.WithCancel
、context.WithTimeout
、context.WithDeadline
创建可取消的context
。这种机制使得在复杂的并发场景中,能够精确控制协程的生命周期,避免出现资源泄露和无效计算。例如在一个爬虫程序中,当用户手动取消任务时,所有正在执行的抓取协程可以通过context
的取消信号停止工作。 - 示例
- 影响:
package main
import (
"context"
"fmt"
"time"
)
func task(ctx context.Context) {
select {
case <-ctx.Done():
fmt.Println("task: cancelled")
case <-time.After(2 * time.Second):
fmt.Println("task: completed")
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
go task(ctx)
time.Sleep(3 * time.Second)
}
- 跨协程数据传递
- 影响:
context
的Value
方法允许在不同协程之间传递请求作用域的数据,如用户认证信息、请求ID等。这种传递方式使得数据可以在整个请求处理的协程树中共享,而无需通过函数参数层层传递。例如在微服务架构中,一个请求在多个服务间传递时,通过context
可以传递跟踪ID,方便进行分布式追踪。 - 示例
- 影响:
package main
import (
"context"
"fmt"
)
func child(ctx context.Context) {
value := ctx.Value("key")
if value != nil {
fmt.Println("child: received value:", value)
}
}
func main() {
ctx := context.WithValue(context.Background(), "key", "value")
go child(ctx)
time.Sleep(1 * time.Second)
}