前言
实现内存同步最常用的方式当然是使用锁(Mutex
,RwLock
,…),但在 golang 中我们知道,对于一个无缓冲的 channel,除非 receiver 准备就绪,否则 sender 的发送会一直阻塞,利用这一点,在一个新的 goroutine 中维护一个需要同步的状态,通过 select
去监听其他 goroutine 中发来的同步请求就可以实现内存同步。
读写分离
首先进行读写操作的抽象:
type readOp struct {
key int
resp chan int
}
type writeOp struct {
key int
val int
resp chan bool
}
然后创建单独的读写 channel:
var readOps uint64 // 记录读的次数
var writeOps uint64 // 记录写的次数
reads := make(chan readOp)
writes := make(chan writeOp)
开辟一个 goroutine 去监听所有的读写请求:
go func() {
var state = make(map[int]int) // 需要同步的状态
for {
select {
case read := <-reads:
read.resp <- state[read.key]
case write := <-writes:
state[write.key] = write.val
write.resp <- true
}
}
}()
开辟 100
个 goroutine 去读,10
个去写:
for r := 0; r < 100; r++ {
go func() {
for {
read := readOp{
key: rand.Intn(5),
resp: make(chan int)}
reads <- read // 当 reads 接收端未准备好时,阻塞
<-read.resp // 取出结果,防止监听阻塞
atomic.AddUint64(&readOps, 1)
time.Sleep(time.Millisecond)
}
}()
}
for w := 0; w < 10; w++ {
go func() {
for {
write := writeOp{
key: rand.Intn(5),
val: rand.Intn(100),
resp: make(chan bool)}
writes <- write // 当 writes 接收端未准备好时,阻塞
<-write.resp // 取出结果,防止监听阻塞
atomic.AddUint64(&writeOps, 1)
time.Sleep(time.Millisecond)
}
}()
}
看看在一秒内能完成多少读写请求:
time.Sleep(time.Second)
readOpsFinal := atomic.LoadUint64(&readOps)
fmt.Println("readOps:", readOpsFinal)
writeOpsFinal := atomic.LoadUint64(&writeOps)
fmt.Println("writeOps:", writeOpsFinal)
输出:
readOps: 82028
writeOps: 8208
版本控制
Version | Action | Time |
---|---|---|
1.0 | Init | 2021-12-31 |
1.0 | Update title | 2022-01-05 |