面试题答案
一键面试可能出现的性能问题
- 共享资源访问冲突:当多个 goroutine 同时访问和修改嵌入式结构体中的共享资源时,可能会导致数据竞争,产生不一致的结果。例如:
package main
import (
"fmt"
"sync"
)
type SharedData struct {
Value int
}
type EmbeddedStruct struct {
SharedData
}
func worker(s *EmbeddedStruct, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 1000; i++ {
s.Value++
}
}
func main() {
var wg sync.WaitGroup
s := EmbeddedStruct{SharedData{0}}
for i := 0; i < 10; i++ {
wg.Add(1)
go worker(&s, &wg)
}
wg.Wait()
fmt.Println("Final value:", s.Value)
}
在上述代码中,多个 worker
goroutine 同时对 s.Value
进行累加操作,由于没有同步机制,最终结果可能不是预期的 10000
。
- 锁争用:为了解决共享资源访问冲突,使用锁机制(如
sync.Mutex
)时,如果锁的粒度设置不当,会导致严重的锁争用问题,降低并发性能。例如:
package main
import (
"fmt"
"sync"
)
type SharedData struct {
Value int
mu sync.Mutex
}
type EmbeddedStruct struct {
SharedData
}
func worker(s *EmbeddedStruct, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 1000; i++ {
s.mu.Lock()
s.Value++
s.mu.Unlock()
}
}
func main() {
var wg sync.WaitGroup
s := EmbeddedStruct{SharedData{0, sync.Mutex{}}}
for i := 0; i < 10; i++ {
wg.Add(1)
go worker(&s, &wg)
}
wg.Wait()
fmt.Println("Final value:", s.Value)
}
这里虽然使用了锁来保护共享资源,但如果 worker
函数中有更多复杂操作,长时间持有锁,会导致其他 goroutine 等待,降低并发效率。
- 并发调度开销:过多的 goroutine 竞争共享资源,会增加 Go 语言运行时的调度开销,影响整体性能。
解决方法
- 优化嵌入式结构体设计:将共享资源分离,降低锁的粒度。例如:
package main
import (
"fmt"
"sync"
)
type SharedData struct {
Value int
mu sync.Mutex
}
type EmbeddedStruct struct {
data []SharedData
}
func worker(s *EmbeddedStruct, index int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 1000; i++ {
s.data[index].mu.Lock()
s.data[index].Value++
s.data[index].mu.Unlock()
}
}
func main() {
var wg sync.WaitGroup
s := EmbeddedStruct{make([]SharedData, 10)}
for i := 0; i < 10; i++ {
wg.Add(1)
go worker(&s, i, &wg)
}
wg.Wait()
total := 0
for _, d := range s.data {
total += d.Value
}
fmt.Println("Final value:", total)
}
这里将共享资源分散到一个数组中,每个元素有自己的锁,降低了锁争用的可能性。
- 合理使用同步原语:除了
sync.Mutex
,还可以根据场景选择sync.RWMutex
(读写锁)。如果读操作远多于写操作,使用读写锁可以提高并发性能。例如:
package main
import (
"fmt"
"sync"
)
type SharedData struct {
Value int
mu sync.RWMutex
}
type EmbeddedStruct struct {
SharedData
}
func reader(s *EmbeddedStruct, wg *sync.WaitGroup) {
defer wg.Done()
s.mu.RLock()
fmt.Println("Read value:", s.Value)
s.mu.RUnlock()
}
func writer(s *EmbeddedStruct, wg *sync.WaitGroup) {
defer wg.Done()
s.mu.Lock()
s.Value++
s.mu.Unlock()
}
func main() {
var wg sync.WaitGroup
s := EmbeddedStruct{SharedData{0, sync.RWMutex{}}}
for i := 0; i < 5; i++ {
wg.Add(1)
go reader(&s, &wg)
}
for i := 0; i < 2; i++ {
wg.Add(1)
go writer(&s, &wg)
}
wg.Wait()
}
这里读操作使用读锁,写操作使用写锁,允许多个读操作同时进行,提高了并发性能。
- 利用 Go 语言并发模型特性:使用
channel
进行数据传递和同步,避免共享资源竞争。例如:
package main
import (
"fmt"
"sync"
)
type EmbeddedStruct struct{}
func worker(ch chan int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 1000; i++ {
ch <- 1
}
}
func main() {
var wg sync.WaitGroup
s := EmbeddedStruct{}
ch := make(chan int)
for i := 0; i < 10; i++ {
wg.Add(1)
go worker(ch, &wg)
}
go func() {
wg.Wait()
close(ch)
}()
total := 0
for val := range ch {
total += val
}
fmt.Println("Final value:", total)
}
这里通过 channel
来传递数据,避免了共享资源的直接竞争,提高了并发安全性和性能。