面试题答案
一键面试- Context的传递方式:
- 在Go语言中,
context.Context
通常通过函数参数在不同的任务(函数)之间传递。对于主任务、子任务和孙任务,主任务创建一个context.Context
,一般是context.WithCancel
创建的可取消的上下文。然后将这个上下文作为参数传递给每个子任务。子任务再将接收到的上下文传递给各自的孙任务。
- 在Go语言中,
- 取消逻辑:
- 主任务通过调用
cancel
函数(context.WithCancel
返回的取消函数)来发起取消操作。这个取消信号会沿着上下文传递链,传递到子任务和孙任务。子任务和孙任务在执行过程中,需要定期检查上下文的取消状态。当检测到取消信号时,应尽快停止正在执行的操作,并返回。
- 主任务通过调用
- 核心代码示例(以Go语言为例):
package main
import (
"context"
"fmt"
"time"
)
// 孙任务函数
func grandChildTask(ctx context.Context, taskID int) {
for {
select {
case <-ctx.Done():
fmt.Printf("Grand - Child Task %d cancelled\n", taskID)
return
default:
fmt.Printf("Grand - Child Task %d is running\n", taskID)
time.Sleep(200 * time.Millisecond)
}
}
}
// 子任务函数
func childTask(ctx context.Context, taskID int) {
// 创建子任务自己的上下文,这样即使子任务提前结束,孙任务也能正确取消
childCtx, childCancel := context.WithCancel(ctx)
defer childCancel()
// 启动多个孙任务
for i := 0; i < 2; i++ {
go grandChildTask(childCtx, taskID*10 + i)
}
for {
select {
case <-ctx.Done():
fmt.Printf("Child Task %d cancelled\n", taskID)
return
default:
fmt.Printf("Child Task %d is running\n", taskID)
time.Sleep(500 * time.Millisecond)
}
}
}
func main() {
// 创建主任务的上下文
ctx, cancel := context.WithCancel(context.Background())
// 启动多个子任务
for i := 0; i < 2; i++ {
go childTask(ctx, i)
}
// 模拟主任务运行一段时间后取消
time.Sleep(2 * time.Second)
cancel()
// 等待一段时间,确保所有任务都有机会处理取消信号
time.Sleep(2 * time.Second)
}
在上述代码中:
main
函数创建了一个可取消的上下文ctx
,并启动了多个子任务。- 每个子任务
childTask
创建了自己的可取消上下文childCtx
,并启动多个孙任务grandChildTask
。 - 子任务和孙任务都通过
select
语句监听上下文的取消信号,当收到取消信号时,打印取消信息并返回,从而实现了主任务取消时,所有子任务和孙任务都能正确且及时地被取消。