sync.RWMutex类型
sync.RWMutex
类型是另外一种互斥体,它是 sync.Mutex
的改进版,在 sync
目录的 rwmutex.go
文件中定义如下:
type RWMutex struct {
w Mutex
writerSem uint32
readerSem uint32
readerCount int32
readerWait int32
}
换句话说,sync.RWMutex
是基于 sync.Mutex
做了必要的添加和改进。
现在让我们来说说 sync.RWMutex
是如果改进 sync.Mutex
的。尽管使用 sync.RWMutex
互斥锁只允许有一个函数执行写操作,但您能有多个属于 sync.RWMutex
互斥锁的读取者。然而,有一件事您要注意:除非sync.RWMutex
互斥锁的所有读取者解锁,您不能为写操作给它上锁,这也是为了实现多互斥锁读取而付出的代价。
RLock()
和 RUnlock()
是出于读取的目的帮您与 sync.RWMutex
互斥体工作的函数,它们分别用于上锁和解锁互斥体。当您为了写操作想要上锁和解锁一个 sync.RWMutex
互斥体时,用在 sync.Mutex
互斥体的 Lock()
和 Unlock()
函数仍适用。因此为了读操作 RLock()
函数应该与 RUnlock()
配对调用。最后,显而易见,您不应该修改在 RLock()
和 RUnlock()
代码块间的任何共享变量。
rwMutex.go
代码说明了 sync.RWMutex
类型的使用和通途。程序分六部分介绍,并且相同的函数有俩个稍有不同的版本。第一个为读操作使用 sync.RWMutex
互斥体,第二个为读操作使用 sync.Mutex
互斥体。这两个函数之间的不同表现会帮助您理解为了读取操作使用 sync.RWMutex
互斥体的好处更多。
rwMutex.go
的第一部分如下:
package main
import (
"fmt"
"os"
"sync"
"time"
)
var Password = secret{password: "myPassword"}
type secret struct {
RWM sync.RWMutex
M sync.Mutex
password string
}
secret
结构有一个共享变量,一个 sync.RWMutex
互斥锁和一个 sync.Mutex
互斥锁。
rwMutex.go
的第二段代码如下:
func Change(c *secret, pass string) {
c.RWM.Lock()
fmt.Println("LChange")
time.Sleep(10 * time.Second)
c.password = pass
c.RWM.Unlock()
}
Change()
函数修改共享变量,意味着您需要使用一个排他锁,这就是使用 Lock()
和 Unlock()
函数的原因。当做变更时离不开使用排他锁!
rwMutex.go
的第三部分如下:
func show(c *secret) string {
c.RWM.RLock()
fmt.Println("show")
time.Sleep(3 * time.Second)
defer c.RWM.RUnlock()
return c.password
}
show()
函数使用 RLock()
和 RUnlock()
函数是因为它的关键部分是用来读取共享变量的。因此,尽管 goroutines 能够读取共享变量,但是在不使用 Lock()
和 Unlock()
函数的情况下,所有 goroutines 都不能修改共享变量。但是,只要有人使用互斥锁读取共享变量,Lock()
函数就会被一直阻塞。
rwMutex.go
的第四段代码如下:
func showWithLock(c *secret) string {
c.M.Lock()
fmt.Println("showWithLock")
time.Sleep(3 * time.Second)
defer c.M.Unlock()
return c.password
}
showWithLock()
函数和 show()
函数之间唯一的不同是 showWithLock()
函数为了读操作使用了排他锁,这意味着只有一个 showWithLock()
函数能读取 secret
结构体的 password
字段。
rwMutex.go
的第五段代码如下:
func main() {
var showFunction = func(c *secret) string {return ""}
if len(os.Args) != 2 {
fmt.Println("Using sync.RWMutex!")
showFunction = show
} else {
fmt.Println("Using sync.Mutex!")
showFunction = showWithLock
}
var waitGroup sync.WaitGroup
fmt.Println("Pass:", showFunction(&Password))
rwMutex.go
的其余代码如下:
for i := 0 ; i < 15; i++ {
waitGroup.Add(1)
go func() {
defer waitGroup.Done()
fmt.Println("Go Pass:", showFunction(&Password))
}()
go func(){
waitGroup.Add(1)
defer waitGroup.Done()
Change(&Password, "123456")
}()
waitGroup.Wait()
fmt.Println("Pass:", showFunction(&Password))
}
}
执行 rwMutex.go
两次并使用 time(1)
命令行工具测试这个程序的两个版本将产生如下输出:
$time go run rwMutex.go 10 >/dev/null
real 0m51.206s
user 0m0.130s
sys 0m0.074s
$time go run rwMutex.go >/dev/null
real 0m22.191s
user 0m0.135s
sys 0m0.071s
注意上面命令结尾处的 > /dev/null
是为了忽略这两个命令的输出。因此,使用 sync.RWMutex
互斥体的版本要比使用 sync.Mutex
的版本快很多。