可能出现的并发问题场景
- 竞态条件(Race Condition):多个协程同时读取和修改状态机的状态,导致最终状态取决于协程执行的顺序,结果不可预测。例如,状态机中有一个计数器
count
,两个协程同时对其进行count++
操作,由于这不是原子操作,可能会丢失一个增量。
解决方案
- 互斥锁(Mutex)
- 使用
sync.Mutex
来保护对状态机状态的访问。在读取或修改状态前,获取锁;操作完成后,释放锁。这样同一时间只有一个协程能访问和修改状态,避免竞态条件。
- 代码示例:
package main
import (
"fmt"
"sync"
)
type StateMachine struct {
state int
mu sync.Mutex
}
func (sm *StateMachine) Transition() {
sm.mu.Lock()
defer sm.mu.Unlock()
sm.state++
}
func main() {
var wg sync.WaitGroup
sm := StateMachine{}
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
sm.Transition()
}()
}
wg.Wait()
fmt.Println("Final state:", sm.state)
}
- 读写锁(RWMutex)
- 如果状态机的读取操作远多于写入操作,可以使用
sync.RWMutex
。读操作时允许多个协程同时进行,写操作时则独占,防止读写冲突和写写冲突。
- 代码示例:
package main
import (
"fmt"
"sync"
)
type StateMachine struct {
state int
rwmu sync.RWMutex
}
func (sm *StateMachine) ReadState() int {
sm.rwmu.RLock()
defer sm.rwmu.RUnlock()
return sm.state
}
func (sm *StateMachine) WriteState() {
sm.rwmu.Lock()
defer sm.rwmu.Unlock()
sm.state++
}
func main() {
var wg sync.WaitGroup
sm := StateMachine{}
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
sm.WriteState()
}()
}
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
state := sm.ReadState()
fmt.Println("Read state:", state)
}()
}
wg.Wait()
}
- 通道(Channel)
- 通过通道来传递对状态机的操作请求,状态机本身在一个单独的协程中处理这些请求,这样就避免了多个协程直接并发访问状态。
- 代码示例:
package main
import (
"fmt"
"sync"
)
type StateMachine struct {
state int
ch chan func()
}
func NewStateMachine() *StateMachine {
sm := &StateMachine{
ch: make(chan func()),
}
go func() {
for f := range sm.ch {
f()
}
}()
return sm
}
func (sm *StateMachine) Transition() {
sm.ch <- func() {
sm.state++
}
}
func main() {
var wg sync.WaitGroup
sm := NewStateMachine()
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
sm.Transition()
}()
}
wg.Wait()
sm.ch <- func() {
fmt.Println("Final state:", sm.state)
}
close(sm.ch)
}