面试题答案
一键面试可能原因
- 共享资源未同步访问:多个
goroutine
可能同时操作共享的资源(如数据库连接池),而defer
语句执行时如果没有适当的同步机制,就会导致资源竞争。例如,一个goroutine
可能在另一个goroutine
还在使用连接时就尝试释放连接到共享连接池,导致数据不一致或连接状态混乱。 - 竞争条件与延迟执行:
defer
语句延迟到函数返回时执行,在函数执行过程中,多个goroutine
对共享资源的操作顺序不确定。如果没有正确的同步,可能会出现竞争条件。比如,两个goroutine
都获取了同一个共享资源,并且都通过defer
语句计划在函数结束时释放它,但是由于执行顺序不确定,可能导致错误的释放操作。
避免方法
- 使用互斥锁(
sync.Mutex
):通过互斥锁来保护共享资源,确保同一时间只有一个goroutine
能够访问共享资源。
package main
import (
"fmt"
"sync"
)
var (
mu sync.Mutex
resource = 0
)
func useResource() {
mu.Lock()
defer mu.Unlock()
// 使用共享资源
resource++
fmt.Println("Using resource, current value:", resource)
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
useResource()
}()
}
wg.Wait()
}
在这个例子中,mu
是一个互斥锁,useResource
函数在使用共享资源 resource
之前先锁定互斥锁,函数结束时通过 defer
语句解锁,这样就避免了多个 goroutine
同时访问 resource
导致的资源竞争。
2. 使用通道(channel
):通过通道来传递共享资源,从而避免直接的共享访问。通道可以控制资源的流向和使用顺序。
package main
import (
"fmt"
"sync"
)
type Resource struct {
value int
}
func main() {
resourceChan := make(chan *Resource, 1)
resource := &Resource{}
resourceChan <- resource
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
res := <-resourceChan
// 使用资源
res.value++
fmt.Println("Using resource, current value:", res.value)
resourceChan <- res
}()
}
wg.Wait()
close(resourceChan)
}
在这个例子中,通过通道 resourceChan
来传递 Resource
实例,每个 goroutine
从通道获取资源,使用后再放回通道,保证了同一时间只有一个 goroutine
在使用资源,避免了资源竞争。同时,通道也可以处理资源的生命周期管理,比如当所有 goroutine
完成后关闭通道。