面试题答案
一键面试设计思路
- 使用
context.Context
来控制生命周期:context.Context
是Go语言中用于传递截止时间、取消信号和其他请求范围的值的机制。通过根context
创建子context
,可以将取消信号传递给所有相关的goroutine
。 - 确保依赖关系正确处理:每个
goroutine
在其执行逻辑中,需要在合适的时机检查context
的取消信号,并且在完成任务后正确地清理资源。 - 使用
sync.WaitGroup
等待所有goroutine
完成:用于同步goroutine
,确保在外部控制取消后,所有goroutine
都有机会进行清理工作并退出。
关键代码片段
package main
import (
"context"
"fmt"
"sync"
"time"
)
func main() {
// 创建一个可取消的context
ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup
// C goroutine
wg.Add(1)
var resultC string
go func(ctx context.Context) {
defer wg.Done()
select {
case <-ctx.Done():
return
case <-time.After(2 * time.Second):
resultC = "Result from C"
}
}(ctx)
// B goroutine
wg.Add(1)
var resultB string
go func(ctx context.Context) {
defer wg.Done()
// 等待C完成
for {
select {
case <-ctx.Done():
return
case <-time.After(100 * time.Millisecond):
if resultC != "" {
select {
case <-ctx.Done():
return
case <-time.After(1 * time.Second):
resultB = "Result from B based on " + resultC
}
return
}
}
}
}(ctx)
// A goroutine
wg.Add(1)
go func(ctx context.Context) {
defer wg.Done()
// 等待B完成
for {
select {
case <-ctx.Done():
return
case <-time.After(100 * time.Millisecond):
if resultB != "" {
select {
case <-ctx.Done():
return
case <-time.After(1 * time.Second):
fmt.Println("Result from A based on ", resultB)
}
return
}
}
}
}(ctx)
// 模拟一段时间后取消
time.Sleep(5 * time.Second)
cancel()
wg.Wait()
fmt.Println("All goroutines have finished.")
}
在上述代码中:
- 首先创建了一个可取消的
context
,并初始化一个WaitGroup
。 C
、B
、A
三个goroutine
依次按照依赖关系执行任务,并在执行过程中不断检查context
的取消信号。- 外部通过
cancel
函数取消context
,WaitGroup
等待所有goroutine
完成清理工作并退出。这样可以有效地管理goroutine
的生命周期,避免资源泄漏和死锁。