并发编程陷阱 - 竞态条件
- 问题阐述:
- 当多个goroutine同时读写共享变量时,就可能出现竞态条件。例如,有一个共享变量
count
,多个goroutine都对其进行加1操作。如果没有适当的同步机制,由于CPU调度的不确定性,可能会导致结果不正确。比如,两个goroutine同时读取count
的值为10,然后各自加1后写回,最终count
的值为11而不是预期的12。
- 解决方案 - 使用互斥锁(Mutex):
- 在Go语言中,可以使用
sync.Mutex
来解决竞态条件问题。以下是示例代码:
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.Mutex
类型的变量mu
被定义。在increment
函数中,通过mu.Lock()
获取锁,这样在同一时间只有一个goroutine能够进入临界区(即对count
进行操作的代码段),操作完成后通过mu.Unlock()
释放锁,从而避免了竞态条件。
另一种解决方案 - 使用读写锁(RWMutex)
- 适用场景:
- 当有大量的读操作和少量的写操作时,使用读写锁(
sync.RWMutex
)会更加高效。读操作可以同时进行,而写操作需要独占锁。
- 示例代码:
package main
import (
"fmt"
"sync"
)
var (
data int
rwmu sync.RWMutex
)
func read(wg *sync.WaitGroup) {
defer wg.Done()
rwmu.RLock()
fmt.Println("Read data:", data)
rwmu.RUnlock()
}
func write(wg *sync.WaitGroup) {
defer wg.Done()
rwmu.Lock()
data++
fmt.Println("Write data:", data)
rwmu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go read(&wg)
}
for i := 0; i < 2; i++ {
wg.Add(1)
go write(&wg)
}
wg.Wait()
}
- 在这个例子中,读操作使用
rwmu.RLock()
获取读锁,可以多个读操作同时进行;写操作使用rwmu.Lock()
获取写锁,此时会阻止其他读写操作,保证数据的一致性。