面试题答案
一键面试- 使用到的Go语言特性:
- Context(上下文):Go语言的
context
包用于在多个goroutine
之间传递截止时间、取消信号等信息。通过context
可以方便地实现超时控制以及在超时时取消所有相关的goroutine
。例如,可以使用context.WithTimeout
函数创建一个带有超时时间的context
。 - WaitGroup:用于等待一组
goroutine
完成。在并发操作中,我们可以使用WaitGroup
来确保所有的协程都执行完毕后再进行资源回收等后续操作。例如,在启动每个协程前调用wg.Add(1)
,在协程结束时调用wg.Done()
,最后使用wg.Wait()
等待所有协程结束。 - Mutex(互斥锁):由于存在共享资源,为了避免竞态条件,需要使用
Mutex
来保护共享资源。在访问共享资源前加锁(mu.Lock()
),访问结束后解锁(mu.Unlock()
)。
- Context(上下文):Go语言的
- 数据结构:
- Map(映射):可以用来管理共享资源,例如,将共享资源的标识符作为键,资源对象作为值存储在
map
中。在Go语言中,标准库的map
不是线程安全的,所以在并发访问时需要结合Mutex
来使用。 - Channel(通道):用于在不同的
goroutine
之间传递数据和信号。例如,可以使用一个无缓冲通道来通知子协程需要取消操作。
- Map(映射):可以用来管理共享资源,例如,将共享资源的标识符作为键,资源对象作为值存储在
- 控制逻辑:
- 创建顶层上下文:
这里ctx, cancel := context.WithTimeout(context.Background(), timeoutDuration) defer cancel()
timeoutDuration
是设定的超时时间。 - 管理协程:
- 启动每个外层协程时,传递上下文
ctx
。例如:
var wg sync.WaitGroup for _, task := range outerTasks { wg.Add(1) go func(ctx context.Context, task Task) { defer wg.Done() // 内层协程管理 var innerWg sync.WaitGroup for _, innerTask := range task.InnerTasks { innerWg.Add(1) go func(ctx context.Context, innerTask InnerTask) { defer innerWg.Done() // 子协程管理 var subWg sync.WaitGroup for _, subTask := range innerTask.SubTasks { subWg.Add(1) go func(ctx context.Context, subTask SubTask) { defer subWg.Done() select { case <-ctx.Done(): // 超时时清理子协程资源 return default: // 执行子协程任务 subTask.Execute() } }(ctx, subTask) } subWg.Wait() select { case <-ctx.Done(): // 超时时清理内层协程资源 return default: // 执行内层协程任务 innerTask.Execute() } }(ctx, innerTask) } innerWg.Wait() select { case <-ctx.Done(): // 超时时清理外层协程资源 return default: // 执行外层协程任务 task.Execute() } }(ctx, task) } wg.Wait()
- 启动每个外层协程时,传递上下文
- 资源回收:
- 在每个协程内部,通过
select
语句监听ctx.Done()
信号。一旦接收到取消信号,立即执行资源清理操作。例如,如果共享资源是文件描述符,关闭文件描述符;如果是网络连接,关闭网络连接等。 - 对于共享资源的清理,在清理前需要加锁,确保在并发环境下不会出现问题。例如:
mu.Lock() // 清理共享资源 delete(sharedResourceMap, resourceID) mu.Unlock()
- 在每个协程内部,通过
- 创建顶层上下文:
通过上述方案,利用Go语言的context
、WaitGroup
、Mutex
等特性,结合合适的数据结构和控制逻辑,可以在整体操作超时时精确地回收所有资源,避免资源泄漏。