面试题答案
一键面试使用defer语句可能遇到的问题
- 资源释放时机不确定:如果多个goroutine中都使用defer语句进行资源清理等延迟任务,由于goroutine的并发特性,无法准确预知每个defer语句执行的具体顺序,可能导致资源在不期望的时间被释放,例如在主程序还需要依赖该资源时就被释放了。
- 资源竞争:当多个goroutine通过defer释放共享资源(如文件描述符、数据库连接等)时,可能会出现资源竞争问题,导致程序崩溃或出现未定义行为。
解决方案
- 使用sync.WaitGroup:通过WaitGroup来等待所有goroutine完成任务,确保所有defer语句都执行完毕后,主程序再退出。这样可以控制资源释放的时机,避免主程序提前退出导致defer语句未执行。
- 使用互斥锁(sync.Mutex):对于共享资源的释放操作,使用互斥锁来保证同一时间只有一个goroutine进行资源释放,避免资源竞争。
示例代码
package main
import (
"fmt"
"sync"
)
func worker(id int, wg *sync.WaitGroup, mu *sync.Mutex) {
defer wg.Done()
// 模拟延迟执行的任务
fmt.Printf("Worker %d started\n", id)
mu.Lock()
defer mu.Unlock()
fmt.Printf("Worker %d finished\n", id)
}
func main() {
var wg sync.WaitGroup
var mu sync.Mutex
numWorkers := 3
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go worker(i, &wg, &mu)
}
wg.Wait()
fmt.Println("All workers completed")
}
在上述代码中:
sync.WaitGroup
用于等待所有goroutine完成。每个goroutine启动时调用wg.Add(1)
,结束时调用wg.Done()
,主程序通过wg.Wait()
等待所有goroutine完成。sync.Mutex
用于保护共享资源(这里虽然没有实际的共享资源操作,但模拟了可能存在资源竞争的场景),确保同一时间只有一个goroutine能执行关键部分代码,避免资源竞争。mu.Lock()
和mu.Unlock()
配对使用,defer mu.Unlock()
保证在函数结束时解锁,即使函数提前返回也能正确解锁。