面试题答案
一键面试Go闭包在并发编程中可能遇到的问题
- 竞态条件:当多个goroutine同时访问和修改共享变量时,由于执行顺序的不确定性,可能导致数据竞争,最终得到不一致或错误的结果。在闭包中,如果它在多个goroutine中被调用且修改外部函数的共享变量,就很容易出现这种情况。
使用sync包避免数据竞争
- 使用
sync.Mutex
:sync.Mutex
用于保护共享资源,确保同一时间只有一个goroutine可以访问该资源。
以下是完整的示例代码:
package main
import (
"fmt"
"sync"
)
func main() {
var count int
var mu sync.Mutex
var wg sync.WaitGroup
increment := func() {
defer wg.Done()
mu.Lock()
count++
mu.Unlock()
}
for i := 0; i < 10; i++ {
wg.Add(1)
go increment()
}
wg.Wait()
fmt.Println("Final count:", count)
}
在上述代码中:
- 定义了一个共享变量
count
和一个sync.Mutex
实例mu
。 increment
闭包函数用于增加count
的值。在修改count
之前,通过mu.Lock()
锁定互斥锁,修改完成后通过mu.Unlock()
解锁,确保同一时间只有一个goroutine可以修改count
。- 使用
sync.WaitGroup
来等待所有的goroutine完成。
- 使用
sync.RWMutex
(如果读操作远多于写操作):如果闭包中对共享变量的操作主要是读操作,偶尔有写操作,可以使用sync.RWMutex
。它允许多个goroutine同时进行读操作,但写操作时会独占资源。
示例代码如下:
package main
import (
"fmt"
"sync"
)
func main() {
var count int
var mu sync.RWMutex
var wg sync.WaitGroup
read := func() {
defer wg.Done()
mu.RLock()
fmt.Println("Current count:", count)
mu.RUnlock()
}
write := func() {
defer wg.Done()
mu.Lock()
count++
mu.Unlock()
}
for i := 0; i < 5; i++ {
wg.Add(1)
go read()
}
for i := 0; i < 2; i++ {
wg.Add(1)
go write()
}
wg.Wait()
}
在这个代码中,read
函数使用mu.RLock()
和mu.RUnlock()
进行读锁定和解锁,允许多个goroutine同时读取count
;write
函数使用mu.Lock()
和mu.Unlock()
进行写锁定和解锁,确保写操作的原子性。