Go语言实现写时阻塞读、读操作互不阻塞的方案问询
解决方案:用读写锁实现读并发、写阻塞读的并发控制
你当前的代码问题出在使用了sync.Mutex互斥锁——它不管是读操作还是写操作,每次都只允许一个goroutine持有锁,导致多个reader之间也会互相阻塞,完全不符合你"reader之间互不阻塞,仅writer修改时阻塞读"的需求。
Go标准库中的sync.RWMutex正好适配这个场景:
- 读锁(
RLock()/RUnlock()):允许多个reader同时持有读锁,互相不会阻塞 - 写锁(
Lock()/Unlock()):当writer获取写锁时,会等待所有正在进行的读操作完成,同时阻塞后续的读和写请求,直到写锁释放,确保数据修改的原子性
下面是修改后的完整代码,同时修复了原代码中的一些小问题(比如time.Seconds拼写错误、goroutine匿名函数写法、main函数提前退出等):
package main import ( "sync" "time" ) var data []int // 模拟读数据的业务逻辑 func read_from_data(d []int) { // 这里可以添加你的读操作,比如打印数据、处理切片等 } // 模拟生成新数据的逻辑 func new_data() []int { return []int{time.Now().Second(), time.Now().Minute()} } func main() { m := sync.RWMutex{} // 将互斥锁替换为读写锁 // 启动10个reader goroutine for i := 0; i < 10; i++ { go func() { for { m.RLock() // 获取读锁,多个reader可同时持有 myData := data // 读取共享数据 m.RUnlock() // 尽早释放读锁,避免不必要地阻塞写操作 // 耗时的读业务逻辑放在锁外执行 read_from_data(myData) time.Sleep(100 * time.Millisecond) // 避免reader过度占用CPU } }() } // 启动writer goroutine go func() { for { newData := new_data() m.Lock() // 获取写锁,此时所有新的读请求会被阻塞 data = newData // 原子性地修改共享数据 m.Unlock() // 释放写锁,读操作恢复并发 time.Sleep(1 * time.Second) // 写操作间隔1秒 } }() // 阻塞main goroutine,避免程序提前退出导致所有goroutine终止 select {} }
几个关键注意点:
- 读锁的释放时机:尽量在读取完共享数据后立刻释放读锁,不要带着读锁执行耗时的业务逻辑(比如
read_from_data),这样能最大程度减少对写操作的阻塞。 - 写锁的排他性:写锁会完全阻塞所有读写操作,所以写操作的逻辑要尽量精简,减少持有写锁的时间。
- main函数的阻塞:原代码中main函数启动goroutine后会直接退出,导致所有goroutine被强制终止,所以用
select {}让main一直运行。
内容的提问来源于stack exchange,提问作者arash kordi
相关产品推荐
相关产品推荐

