Go读写锁操作方法示例详解

2022-11-13 11:11:07 示例 读写 详解

引言

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

相关文章