面试题答案
一键面试实现方案
- 使用
WaitGroup
控制任务完成同步:WaitGroup
用于等待一组协程完成任务。对于存在依赖关系的任务,例如数据处理任务等待数据采集任务完成,可以让数据采集任务完成后调用wg.Done()
,数据处理任务在开始前调用wg.Wait()
。 - 使用
Channel
控制并发数量:创建一个带缓冲的Channel
,其缓冲区大小即为允许同时运行的最大协程数量。每个协程在启动前尝试从该Channel
获取一个值,任务完成后再向Channel
发送一个值,这样就可以限制同一时间运行的协程数量。 - 使用
Mutex
保护共享资源:如果不同任务需要访问共享资源(如共享的数据结构),则使用Mutex
来确保数据的一致性,避免竞态条件。
以下是示例代码:
package main
import (
"fmt"
"sync"
)
// 限制同时运行的协程数量
const maxConcurrent = 3
func main() {
var wg sync.WaitGroup
semaphore := make(chan struct{}, maxConcurrent)
// 模拟数据采集任务
for i := 0; i < 5; i++ {
semaphore <- struct{}{} // 获取信号量
wg.Add(1)
go func(id int) {
defer func() {
<-semaphore // 释放信号量
wg.Done()
}()
fmt.Printf("Data collection task %d is running\n", id)
// 模拟数据采集操作
}(i)
}
// 等待所有数据采集任务完成
wg.Wait()
// 模拟数据处理任务,依赖数据采集任务完成
for i := 0; i < 3; i++ {
semaphore <- struct{}{} // 获取信号量
wg.Add(1)
go func(id int) {
defer func() {
<-semaphore // 释放信号量
wg.Done()
}()
fmt.Printf("Data processing task %d is running\n", id)
// 模拟数据处理操作
}(i)
}
// 等待所有数据处理任务完成
wg.Wait()
}
性能优势
- 资源合理利用:通过限制并发协程数量,可以避免系统资源过度消耗,例如CPU和内存,提高系统整体性能。
- 高效的同步机制:
WaitGroup
和Channel
的使用提供了高效的同步机制,能够准确控制任务的执行顺序和并发数量,减少不必要的等待时间。
可靠性优势
- 数据一致性:
Mutex
的使用确保了共享资源的安全访问,避免了竞态条件,从而保证了数据的一致性和可靠性。 - 任务依赖管理:通过
WaitGroup
实现任务之间的依赖关系,确保依赖的任务完成后再执行后续任务,提高了系统的稳定性。
潜在问题
- 死锁风险:如果在使用
WaitGroup
、Channel
和Mutex
时逻辑错误,例如忘记调用wg.Done()
或在错误的地方获取/释放信号量,可能会导致死锁。 - 性能瓶颈:虽然限制并发数量有助于资源管理,但如果设置的并发数量过小,可能会导致任务执行时间过长,形成性能瓶颈。
- 代码复杂性:使用多种并发原语会增加代码的复杂性,使得代码的理解、调试和维护变得更加困难。