面试题答案
一键面试使用WaitGroup设计解决方案
以下是一个使用Go语言和WaitGroup来解决该问题的示例代码,假设使用Go语言进行开发:
package main
import (
"fmt"
"sync"
)
// 定义任务函数
func task1(wg *sync.WaitGroup, resultChan chan int) {
defer wg.Done()
// 模拟任务1的执行
result := 10
resultChan <- result
}
func task2(wg *sync.WaitGroup, resultChan chan int, depResult int) {
defer wg.Done()
// 依赖task1的结果
newResult := depResult + 5
resultChan <- newResult
}
func main() {
var wg sync.WaitGroup
resultChan1 := make(chan int)
resultChan2 := make(chan int)
// 启动任务1
wg.Add(1)
go task1(&wg, resultChan1)
// 等待任务1完成并获取结果
go func() {
wg.Wait()
close(resultChan1)
close(resultChan2)
}()
depResult := <-resultChan1
// 启动任务2,依赖任务1的结果
wg.Add(1)
go task2(&wg, resultChan2, depResult)
// 获取任务2的结果
finalResult := <-resultChan2
fmt.Printf("Final result: %d\n", finalResult)
}
并发安全陷阱及避免方法
- 未正确使用WaitGroup:
- 陷阱:忘记调用
wg.Add
增加计数,或者在任务结束前未调用wg.Done
减少计数,会导致wg.Wait
永远阻塞或提前返回。 - 避免方法:确保在启动每个协程前调用
wg.Add(1)
,并且在每个协程的函数中使用defer wg.Done()
来保证函数结束时计数减少。
- 陷阱:忘记调用
- 数据竞争:
- 陷阱:多个协程同时访问和修改共享变量时可能发生数据竞争,例如在任务函数中共享一个变量并同时读写。
- 避免方法:使用互斥锁(
sync.Mutex
)来保护共享变量,或者使用通道(chan
)进行数据传递,避免直接共享数据。
- 通道操作不当:
- 陷阱:如果通道未正确关闭,可能会导致接收操作永远阻塞。另外,如果向已关闭的通道发送数据,会导致运行时恐慌。
- 避免方法:在所有发送操作完成后关闭通道,并且在接收端使用
for - range
循环或者ok
-idiom(value, ok := <-chan
)来处理通道关闭的情况。