面临的新挑战
- 数据竞争:多个goroutine可能同时访问和修改同一个实参,导致数据不一致。例如,一个goroutine读取实参的某个字段值,而另一个goroutine同时修改该字段,这就可能造成读取到的是一个中间状态的值。
- 未初始化或部分初始化:在并发场景下,实参可能在未完全初始化时就被传递给其他goroutine使用,导致程序出现错误行为。
避免并发安全问题的设计方法
- 互斥锁(Mutex):使用
sync.Mutex
来保护对实参的访问。在读取或修改实参前先获取锁,操作完成后释放锁。
package main
import (
"fmt"
"sync"
)
var mu sync.Mutex
var count int
func increment(wg *sync.WaitGroup) {
mu.Lock()
count++
mu.Unlock()
wg.Done()
}
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)
}
- 读写锁(RWMutex):当对实参的操作读多写少的场景下,使用
sync.RWMutex
。读操作时可以多个goroutine同时进行,写操作时则需要独占锁。
package main
import (
"fmt"
"sync"
)
var rwmu sync.RWMutex
var data string
func readData(wg *sync.WaitGroup) {
rwmu.RLock()
fmt.Println("Read data:", data)
rwmu.RUnlock()
wg.Done()
}
func writeData(wg *sync.WaitGroup) {
rwmu.Lock()
data = "new data"
rwmu.Unlock()
wg.Done()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go readData(&wg)
}
for i := 0; i < 2; i++ {
wg.Add(1)
go writeData(&wg)
}
wg.Wait()
}
- 通道(Channel):通过通道在goroutine之间传递数据,保证数据的顺序性和安全性。通道会对数据进行缓冲和同步,避免数据竞争。
package main
import (
"fmt"
)
func sender(ch chan int) {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}
func receiver(ch chan int) {
for num := range ch {
fmt.Println("Received:", num)
}
}
func main() {
ch := make(chan int)
go sender(ch)
receiver(ch)
}