golang读写锁RWMutex

本文介绍了读写锁的基本概念及其实现原理。通过两个示例详细展示了读写锁在多线程环境下如何保证读操作的并发性和写操作的排他性。适合希望了解并发编程中读写锁特性的开发者阅读。

读写锁是针对读写的互斥锁

基本遵循两大原则:

1、可以随便读,多个goroutine同时读

2、写的时候,啥也不能干。不能读也不能写

RWMutex提供了四个方法:

func (*RWMutex) Lock // 写锁定

func (*RWMutex) Unlock // 写解锁

func (*RWMutex) RLock // 读锁定

func (*RWMutex) RUnlock // 读解锁

一、随便读

package main

import (
    "sync"
    "time"
)

var m *sync.RWMutex

func main() {
    m = new(sync.RWMutex)
    
    // 多个同时读
    go read(1)
    go read(2)

    time.Sleep(2*time.Second)
}

func read(i int) {
    println(i,"read start")

    m.RLock()
    println(i,"reading")
    time.Sleep(1*time.Second)
    m.RUnlock()    

    println(i,"read over")
}
运行结果:

1 read start

1 reading 

2 read start

2 reading

1 read over

2 read over

可以看出1 读还没有结束,2已经在读


二、写的时候啥也不能干

package main

import (
    "sync"
    "time"
)

var m *sync.RWMutex

func main() {
    m = new(sync.RWMutex)
    
    // 写的时候啥也不能干
    go write(1)
    go read(2)
    go write(3)

    time.Sleep(2*time.Second)
}

func read(i int) {
    println(i,"read start")

    m.RLock()
    println(i,"reading")
    time.Sleep(1*time.Second)
    m.RUnlock()    

    println(i,"read over")
}

func write(i int) {
    println(i,"write start")

    m.Lock()
    println(i,"writing")
    time.Sleep(1*time.Second)
    m.Unlock()

    println(i,"write over")
}
输出:

1 write start

1 writing

2 read start

3 write start

1 writing over

2 reading

2 read over

3 writing

3 write over

可以看出1在写时,2没法读,3也没法写

ps:

在测试写锁时,本人的输出结果为:

2 read start
2 reading
1 write start
3 write start
2 read over
1 writing
1 write over
3 writing
3 write over
可以看出2在读时,1、3都没法写


转载地址:http://studygolang.com/articles/2497

### 读写锁 `sync.RWMutex` 与普通锁 `sync.Mutex` 的区别 在 Go 语言中,`sync.Mutex` 和 `sync.RWMutex` 是用于控制并发访问的同步机制。它们之间的主要区别体现在锁定策略、适用场景以及性能特征上。 #### 锁定策略 `sync.Mutex` 是互斥锁,它在同一时刻只允许一个 goroutine 对共享资源进行访问,无论该访问是读操作还是写操作。这种锁适用于读写不确定的场景,即读写次数没有明显区别,并且只允许一个读或写的场景[^2]。因此,它也被称为**全局锁**。 ```go var L sync.Mutex L.Lock() // 执行临界区代码 L.Unlock() ``` 相比之下,`sync.RWMutex` 是读写锁,它允许多个 goroutine 同时读取共享资源,但在写操作发生时会阻止所有读操作和写操作。这种锁适用于**读多写少**的场景,例如缓存系统、配置管理等。`sync.RWMutex` 提供了 `RLock()` 和 `RUnlock()` 方法来支持并发读取,而 `Lock()` 和 `Unlock()` 方法则用于独占写入[^2]。 ```go var rwMu sync.RWMutex // 读操作 rwMu.RLock() // 读取数据 rwMu.RUnlock() // 写操作 rwMu.Lock() // 修改数据 rwMu.Unlock() ``` #### 性能表现 从性能角度来看,`sync.RWMutex` 在读多写少的场景下通常优于 `sync.Mutex`。因为多个 goroutine 可以同时持有读锁,从而减少线程阻塞的时间。然而,在写操作频繁的情况下,`sync.RWMutex` 的性能可能会下降,因为它需要等待所有读操作完成才能获取写锁。此外,如果存在大量写操作,可能会导致“写饥饿”问题(write starvation),即写操作长时间无法获得锁,因为总是有新的读操作在进入[^3]。 另一方面,`sync.Mutex` 虽然不支持并发读取,但它的实现较为简单,因此在某些低并发或写操作较多的场景下可能具有更好的性能表现。尤其是在只需要保护少量数据或关键路径的情况下,使用 `sync.Mutex` 可以避免不必要的复杂性[^1]。 #### 使用场景 - **`sync.Mutex` 适用场景**: - 数据结构被频繁修改(写操作多于读操作)。 - 不希望引入额外的复杂度来处理读写分离。 - 需要确保只有一个 goroutine 能够访问共享资源,无论其是读还是写操作。 - **`sync.RWMutex` 适用场景**: - 共享资源主要用于读取,偶尔才会被修改(读操作远多于写操作)。 - 希望提高并发性能,允许同时进行多个读操作。 - 需要区分对待读操作和写操作,以优化整体吞吐量。 #### 注意事项 在使用 `sync.Mutex` 或 `sync.RWMutex` 时需要注意以下几点: - 如果在一个 goroutine 中多次对同一个 `sync.Mutex` 进行加锁而未解锁,则会导致死锁[^2]。 - `sync.Mutex` 和 `sync.RWMutex` 都不是可重入锁(reentrant lock),这意味着即使同一个 goroutine 已经持有了锁,也不能再次调用 `Lock()` 或 `RLock()`,除非之前已经释放了该锁。 - 在使用 `sync.RWMutex` 时,应尽量减少写操作的持续时间,以避免影响其他 goroutine 的读取效率[^4]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值