可能遇到的问题场景
- 竞争条件:多个 goroutine 同时访问和修改共享变量。例如在一个银行转账操作中,多个 goroutine 可能同时读取账户余额,并基于旧的余额进行扣除操作,导致数据不一致。
- 数据不一致:当一个 goroutine 修改了共享数据,但其他 goroutine 未及时感知到这种变化,就会出现数据不一致。比如一个缓存更新操作,一个 goroutine 更新了缓存,但另一个 goroutine 仍然从旧的缓存数据读取。
解决方案
- 使用同步原语 - mutex(互斥锁)
- 设计思路:通过在访问共享资源前加锁,访问完后解锁,确保同一时间只有一个 goroutine 能访问共享资源。
- 代码示例:
package main
import (
"fmt"
"sync"
)
var (
counter int
mu sync.Mutex
)
func increment(wg *sync.WaitGroup) {
defer wg.Done()
mu.Lock()
counter++
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 counter value:", counter)
}
- 使用 channel
- 设计思路:通过 channel 进行数据传递,避免共享变量。goroutine 之间通过发送和接收数据来同步操作,确保数据一致性。
- 代码示例:
package main
import (
"fmt"
)
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Printf("Worker %d started job %d\n", id, j)
result := j * 2
fmt.Printf("Worker %d finished job %d, result: %d\n", id, j, result)
results <- result
}
}
func main() {
const numJobs = 5
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
for j := 1; j <= numJobs; j++ {
jobs <- j
}
close(jobs)
for a := 1; a <= numJobs; a++ {
<-results
}
close(results)
}
- 使用 sync.RWMutex(读写互斥锁)
- 设计思路:适用于读多写少的场景。允许多个 goroutine 同时读共享资源,但写操作时需要独占锁,防止读写冲突和写写冲突。
- 代码示例:
package main
import (
"fmt"
"sync"
)
var (
data = make(map[string]int)
rwmu sync.RWMutex
)
func read(key string, wg *sync.WaitGroup) {
defer wg.Done()
rwmu.RLock()
value, exists := data[key]
rwmu.RUnlock()
if exists {
fmt.Printf("Read key %s, value: %d\n", key, value)
} else {
fmt.Printf("Key %s not found\n", key)
}
}
func write(key string, value int, wg *sync.WaitGroup) {
defer wg.Done()
rwmu.Lock()
data[key] = value
rwmu.Unlock()
fmt.Printf("Write key %s, value: %d\n", key, value)
}
func main() {
var wg sync.WaitGroup
wg.Add(1)
go write("key1", 100, &wg)
for i := 0; i < 3; i++ {
wg.Add(1)
go read("key1", &wg)
}
wg.Wait()
}