引言
前面讲到,在资源竞争的时候可以使用互斥锁,保证了资源访问的唯一性,但也降低了性能,仔细分析一下场景,如果只是读取数据,无论多少个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读写锁方法的资料请关注编程网其它相关文章!