面试题答案
一键面试竞争问题分析
在高并发场景下,当多个 goroutine 对共享资源进行读写操作时,可能会出现竞态条件(race condition)。这是因为多个 goroutine 可能同时访问和修改共享资源,导致数据的不一致。例如,一个 goroutine 正在读取共享资源时,另一个 goroutine 同时对其进行修改,就可能导致读取到的数据不正确。
优化方法
- 使用 sync.Mutex:
sync.Mutex
是 Go 语言提供的互斥锁,用于保护共享资源。在访问共享资源前加锁,访问完成后解锁,这样同一时间只有一个 goroutine 能访问共享资源,从而避免竞态条件。- 代码示例:
package main
import (
"fmt"
"sync"
)
var (
counter int
mu sync.Mutex
)
func generator(wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 10; i++ {
mu.Lock()
counter++
mu.Unlock()
}
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go generator(&wg)
}
wg.Wait()
fmt.Println("Final counter:", counter)
}
- 使用 channel:
- channel 是 Go 语言中用于 goroutine 之间通信的机制。通过 channel 传递数据,可以避免共享资源的直接竞争。数据在不同 goroutine 之间通过 channel 传递,而不是共享内存。
- 代码示例:
package main
import (
"fmt"
"sync"
)
func generator(ch chan int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
}
func consumer(ch chan int, wg *sync.WaitGroup) {
defer wg.Done()
for val := range ch {
fmt.Println("Consumed:", val)
}
}
func main() {
var wg sync.WaitGroup
ch := make(chan int)
wg.Add(1)
go generator(ch, &wg)
for i := 0; i < 5; i++ {
wg.Add(1)
go consumer(ch, &wg)
}
wg.Wait()
}
- 使用 sync.RWMutex:
- 如果读操作远多于写操作,可以使用
sync.RWMutex
。它允许同时有多个读操作,但写操作时会独占资源,从而保证数据一致性。 - 代码示例:
- 如果读操作远多于写操作,可以使用
package main
import (
"fmt"
"sync"
)
var (
data int
rwmu sync.RWMutex
)
func reader(wg *sync.WaitGroup) {
defer wg.Done()
rwmu.RLock()
fmt.Println("Read data:", data)
rwmu.RUnlock()
}
func writer(wg *sync.WaitGroup) {
defer wg.Done()
rwmu.Lock()
data++
rwmu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go reader(&wg)
}
for i := 0; i < 2; i++ {
wg.Add(1)
go writer(&wg)
}
wg.Wait()
}
通过合理运用这些 Go 语言的特性,可以有效地解决高并发场景下的竞争问题,确保数据的一致性和程序的高效运行。