面试题答案
一键面试设计思路
- 创建上下文:使用
context.Background()
作为根上下文,在此基础上衍生出带有取消功能的上下文ctx, cancel := context.WithCancel(context.Background())
。 - 传递上下文:将上下文传递给每个并发任务的函数,这样每个任务都可以感知到取消信号。
- 任务间依赖:利用通道(channel)来传递任务间的数据和依赖关系,确保数据安全传递。
- 有序取消:当需要取消任务时,调用
cancel()
函数,所有接收到该上下文的任务应在合适的时机检查上下文的取消信号并退出。
关键代码实现
package main
import (
"context"
"fmt"
"time"
)
// 模拟一个有依赖的任务
func task1(ctx context.Context, ch chan int) {
select {
case <-ctx.Done():
return
case ch <- 1: // 向通道发送数据
fmt.Println("Task1 completed and sent data")
}
}
// 依赖task1结果的任务
func task2(ctx context.Context, ch1 chan int, ch2 chan int) {
select {
case <-ctx.Done():
return
case data := <-ch1:
// 对task1的数据进行处理
result := data * 2
select {
case <-ctx.Done():
return
case ch2 <- result:
fmt.Println("Task2 completed and sent data")
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ch1 := make(chan int)
ch2 := make(chan int)
go task1(ctx, ch1)
go task2(ctx, ch1, ch2)
// 主程序等待一段时间
time.Sleep(2 * time.Second)
// 取消任务
cancel()
// 关闭通道
close(ch1)
close(ch2)
// 输出最终结果
for result := range ch2 {
fmt.Printf("Final result: %d\n", result)
}
}
在上述代码中:
task1
和task2
通过context.Context
来感知取消信号。task1
和task2
之间通过通道ch1
和ch2
传递数据,保证数据安全。- 主程序通过调用
cancel()
函数实现任务的有序取消,并通过关闭通道来确保资源释放和防止死锁。