可能导致资源竞争的场景:
- 多个 goroutine 同时读写共享变量:例如多个 goroutine 同时对一个全局变量进行读写操作,读操作和写操作没有顺序控制,就可能出现读操作读取到部分写入的数据,导致数据不一致。
- 对共享的 map 进行并发读写:Go 语言的 map 本身不是线程安全的,多个 goroutine 同时对一个 map 进行读和写,或者同时进行多个写操作,可能导致程序崩溃或者数据混乱。
规避方法:
- 使用
sync.Mutex
:
- 原理:
sync.Mutex
是一种互斥锁,它可以保证在同一时刻只有一个 goroutine 能够访问共享资源。
- 示例:
package main
import (
"fmt"
"sync"
)
var (
count int
mu sync.Mutex
)
func increment(wg *sync.WaitGroup) {
defer wg.Done()
mu.Lock()
count++
mu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go increment(&wg)
}
wg.Wait()
fmt.Println("Final count:", count)
}
- 使用
sync.RWMutex
:
- 原理:
sync.RWMutex
是读写互斥锁,允许多个 goroutine 同时进行读操作,但只允许一个 goroutine 进行写操作。当有写操作时,其他读和写操作都被阻塞。
- 示例:
package main
import (
"fmt"
"sync"
)
var (
data = make(map[string]string)
rwmu sync.RWMutex
)
func read(key string, wg *sync.WaitGroup) {
defer wg.Done()
rwmu.RLock()
value := data[key]
fmt.Printf("Read key %s, value %s\n", key, value)
rwmu.RUnlock()
}
func write(key, value string, wg *sync.WaitGroup) {
defer wg.Done()
rwmu.Lock()
data[key] = value
fmt.Printf("Write key %s, value %s\n", key, value)
rwmu.Unlock()
}
func main() {
var wg sync.WaitGroup
wg.Add(2)
go write("name", "John", &wg)
go read("name", &wg)
wg.Wait()
}
- 使用
sync.Map
:
- 原理:
sync.Map
是 Go 语言提供的线程安全的 map 实现,它内部使用了类似分段锁的机制,允许多个 goroutine 并发地进行读写操作。
- 示例:
package main
import (
"fmt"
"sync"
)
func main() {
var m sync.Map
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
m.Store("name", "John")
}()
go func() {
defer wg.Done()
value, ok := m.Load("name")
if ok {
fmt.Printf("Load key name, value %s\n", value.(string))
}
}()
wg.Wait()
}