面试题答案
一键面试可能遇到的问题
- 数据竞争与死锁:
- 当多个goroutine通过defer语句访问共享数据,且在defer语句执行时没有正确使用同步机制(如mutex),可能会导致数据竞争,使得程序出现不可预测的结果。
- 若在defer语句中进行复杂的同步操作,比如嵌套锁操作,可能会因为锁的获取顺序不一致导致死锁。例如,goroutine A获取锁1,然后在defer中尝试获取锁2,而goroutine B获取锁2,然后在defer中尝试获取锁1,就可能发生死锁。
- 执行顺序不确定性:
- 由于goroutine的并发特性,不同goroutine中defer语句的执行顺序是不确定的。当存在数据共享时,这可能导致依赖于特定执行顺序的操作出现错误。比如,一个goroutine在defer中释放资源,另一个goroutine在defer中需要使用这个资源,如果执行顺序不当,可能导致资源已释放而无法使用。
解决方案
- 正确使用同步机制:
- 确保在defer语句中对共享数据的访问使用正确的同步机制,如
sync.Mutex
。在获取数据前锁定,操作完成后解锁,以避免数据竞争。 - 谨慎设计锁的获取和释放逻辑,避免死锁。一种常见的方法是使用固定的锁获取顺序,比如总是先获取编号小的锁,再获取编号大的锁。
- 确保在defer语句中对共享数据的访问使用正确的同步机制,如
- 明确执行顺序:
- 使用
sync.WaitGroup
等同步工具来明确goroutine的执行顺序。通过WaitGroup
的Add
、Done
和Wait
方法,可以确保所有goroutine完成特定任务后再继续执行后续代码,从而控制defer语句的执行顺序。
- 使用
代码示例
package main
import (
"fmt"
"sync"
)
var (
mu sync.Mutex
count int
)
func worker(wg *sync.WaitGroup) {
defer wg.Done()
mu.Lock()
defer mu.Unlock()
count++
fmt.Printf("Worker incremented count to %d\n", count)
}
func main() {
var wg sync.WaitGroup
numWorkers := 5
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go worker(&wg)
}
wg.Wait()
fmt.Printf("Final count: %d\n", count)
}
在上述代码中:
- 同步机制:使用
sync.Mutex
来保护共享变量count
,确保在defer语句(解锁操作)执行前,对count
的操作是安全的,避免数据竞争。 - 执行顺序控制:通过
sync.WaitGroup
来确保所有goroutine完成工作后,主函数才继续执行,从而控制了defer语句的执行顺序,避免因goroutine执行顺序不确定导致的问题。