- 前言
- Go 与操作系统
- Go 内部机制
- Go 基本数据类型
- 4 组合类型的使用
- 5 数据结构
- 6 Go package 中不为人知的知识
- 7 反射和接口
- 8 Go UNIX 系统编程
- 08.1 关于 UNIX 进程
- 08.2 flag 包
- 8.2 flag 包
- 08.3 io.Reader 和 io.Writer 接口
- 08.4 bufio 包
- 08.5 读取文本文件
- 08.6 从文件中读取所需的数据量
- 08.7 为什么我们使用二进制格式
- 08.8 读取 CSV 文件
- 08.9 写入文件
- 08.10 从磁盘加载和保存数据
- 08.11 再看strings包
- 08.12 关于bytes包
- 08.13 文件权限
- 08.14 处理 Unix 信号
- 08.15 Unix 管道编程
- 08.16 遍历目录树
- 08.17 使用 ePBF
- 08.18 关于 syscall.PtraceRegs
- 08.19 跟踪系统调用
- 08.20 User ID 和 group ID
- 08.21 其他资源
- 08.22 练习
- 08.23 总结
- 9 并发 Goroutines、Channel 和 Pipeline
- 10 Go 并发-进阶讨论
- 11 代码测试、优化及分析
- 12 Go 网络编程基础
- 13 网络编程 - 构建服务器与客户端
10.5.2 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
的版本快很多。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论