面试题答案
一键面试潜在陷阱分析
- 竞态条件:多个goroutine同时访问和修改共享资源,导致最终结果不可预测。例如,多个goroutine同时对一个全局变量进行累加操作。
- 资源竞争:当多个goroutine竞争有限的资源(如文件描述符、数据库连接等)时,可能导致资源分配不均或资源耗尽。
- 闭包与共享变量:在闭包中使用共享变量时,如果多个goroutine同时调用该闭包,可能会因为共享变量的状态不一致而产生问题。
陷阱出现场景代码示例
package main
import (
"fmt"
)
var counter int
func increment() {
counter++
}
func main() {
for i := 0; i < 1000; i++ {
go increment()
}
fmt.Println("Final counter value:", counter)
}
在上述代码中,多个goroutine同时调用increment
函数对counter
进行累加。由于没有同步机制,最终的counter
值往往小于1000,因为多个goroutine同时读取和修改counter
,产生了竞态条件。
解决方案
- 互斥锁(Mutex):使用
sync.Mutex
来保护共享资源,确保同一时间只有一个goroutine可以访问共享资源。
package main
import (
"fmt"
"sync"
)
var (
counter int
mu sync.Mutex
)
func increment() {
mu.Lock()
counter++
mu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println("Final counter value:", counter)
}
- 读写锁(RWMutex):如果对共享资源的读操作远多于写操作,可以使用
sync.RWMutex
。读操作可以并发进行,写操作则需要独占访问。
package main
import (
"fmt"
"sync"
)
var (
data int
rwmu sync.RWMutex
)
func read() int {
rwmu.RLock()
defer rwmu.RUnlock()
return data
}
func write() {
rwmu.Lock()
data++
rwmu.Unlock()
}
- 通道(Channel):通过通道进行数据传递,避免共享资源。每个goroutine处理自己的数据副本,通过通道将结果传递出去。
package main
import (
"fmt"
)
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Printf("Worker %d started job %d\n", id, j)
result := j * 2
fmt.Printf("Worker %d finished job %d with result %d\n", id, j, result)
results <- result
}
}
func main() {
const numJobs = 5
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
for j := 1; j <= numJobs; j++ {
jobs <- j
}
close(jobs)
for a := 1; a <= numJobs; a++ {
<-results
}
close(results)
}
- 原子操作:对于简单的数值类型,可以使用
sync/atomic
包进行原子操作,避免使用锁的开销。
package main
import (
"fmt"
"sync"
"sync/atomic"
)
var counter int64
func increment() {
atomic.AddInt64(&counter, 1)
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println("Final counter value:", atomic.LoadInt64(&counter))
}