引言
前面讲到,在资源竞争的时候可以使用互斥锁,保证了资源访问的唯一性,但也降低了性能,仔细分析一下场景,如果只是读取数据,无论多少个goroutine都是不会存在逻辑上的互斥操作的。
这里读写锁 RWMutex就应运而生了,RWMutex可以分别针对读操作和写操作进行上锁和解锁。
RWMutex同一时刻允许多个读操作进行,但只允许一个写操作进行,同时,在某一个写操作进行的时候,读操作不可进行。
读写锁有很多方法
- 方法一: RLock 这个方法是读锁,当写锁存在的时候,无法加载读锁,只有当不存在锁,或者只有读锁的时候才能使用。读锁可以同时加载多个,适用于多度写少的场景。
- 方法二: RUnlock 这个方法是读解锁,用来撤销单次的读锁操作。
- 方法三: Lock 这个方法是写上锁,如果在添加写上锁之前已经有其他的读锁和写锁了,此时,这个Lock会被阻塞,直到可以使用。
- 方法四: Unlock 这个方法是写解锁,如果没有绑定写锁就直接写解锁,会引发运行时错误。
读操作
下面用实际的代码做例子,看一下读操作:
package main import ( "fmt" "sync" "time" ) //新建一个锁对象的指针,然后待会儿再指针中创建这个锁的对象 var rwMutex *sync.RWMutex //为了保证 子的goroutine先执行,可以使用同步等待组wg,这里创建wg的指针类型 var wg *sync.WaitGroup func main() { rwMutex = new(sync.RWMutex) wg = new(sync.WaitGroup) wg.Add(2)//这里记得+add // 在主函数中 启动2条goroutine go readData(1) go readData(2) wg.Wait() fmt.Println("main func end") } func readData(i int) { defer wg.Done() fmt.Println(i, "start locking!") // 给读操作 上锁 rwMutex.RLock() // 读数据 fmt.Println(i, "Reading data") // 睡一下 time.Sleep(1 * time.Second) // 读解锁 rwMutex.RUnlock() //打印提示信息 fmt.Println(i, "Read over") }
代码运行结果如下:
2 start locking!
2 Reading data
1 start locking!
1 Reading data
2 Read over
1 Read over
main func end
从打印结果可知,第二条goroutine先上读锁,然后第二条开始读取,然后第一条上读锁【从这里就可以看出,因为第二条的读锁还没读解锁,第一条的读锁就上了,所以这里的读锁并不互斥】,之后第一条开始读取,第二条读解锁,第一条读解锁。主goroutine结束。
写操作
package main import ( "fmt" "sync" "time" ) //新建一个锁对象的指针,然后待会儿再指针中创建这个锁的对象 var rwMutex *sync.RWMutex //为了保证 子的goroutine先执行,可以使用同步等待组wg,这里创建wg的指针类型 var wg *sync.WaitGroup func main() { rwMutex = new(sync.RWMutex) wg = new(sync.WaitGroup) wg.Add(4) // 在主函数中 启动2条goroutine go readData(1) go readData(2) go writeData(3) go writeData(4) wg.Wait() fmt.Println("main func end") } func readData(i int) { defer wg.Done() fmt.Println(i, "start locking!") // 给读操作 上锁 rwMutex.RLock() // 读数据 fmt.Println(i, "Reading data") // 睡一下 time.Sleep(1 * time.Second) // 读解锁 rwMutex.RUnlock() //打印提示信息 fmt.Println(i, "Read over") } func writeData(i int) { defer wg.Done() fmt.Println(i, " Writing Start") //写上锁 rwMutex.Lock() fmt.Println(i, "~~~ writing right now~~~") time.Sleep(1 * time.Second) rwMutex.Unlock() fmt.Println(i, "writing completed") }
代码运行结果如下:
2 start locking!
2 Reading data
4 Writing Start
1 start locking!
3 Writing Start
2 Read over
4 ~~~ writing right now~~~
4 writing completed
1 Reading data
1 Read over
3 ~~~ writing right now~~~
3 writing completed
main func end
分析可知,只有在goroutine4结束写之后goroutine3才拿到权限开始写。
不过讲真,到处都是lock 和 unlock,临界区什么的,真的是臃肿又容易出问题。
go语言中多个协程想共享数据的时候,将会有更加优雅的处理方式。
以上就是Go读写锁方法示例详解的详细内容,更多关于Go读写锁方法的资料请关注脚本之家其它相关文章!