26 | sync.Mutex与sync.RWMutex
该思维导图由 AI 生成,仅供参考
前导内容: 竞态条件、临界区与同步工具
- 深入了解
- 翻译
- 解释
- 总结
本文深入介绍了Go语言中的sync.Mutex和sync.RWMutex,这两个类型是sync包中最重要且常用的同步工具。文章首先解释了竞态条件、临界区和同步工具的概念,强调了在多线程共享数据时可能出现的问题,以及同步工具的重要性。接着详细介绍了互斥锁的作用和使用方法,包括锁定和解锁操作的示例代码,并列举了使用互斥锁时需要注意的事项。重点强调了在并发编程中如何使用互斥锁来保护临界区,以及使用互斥锁的注意事项。文章还提到了避免重复锁定和解锁互斥锁的重要性,以及对互斥锁的传递和副本问题。总的来说,本文内容详实,适合读者快速了解并发编程中的同步工具以及互斥锁的基本原理和使用方法。 读写锁是读/写互斥锁的简称,它可以对读操作和写操作施加不同程度的保护,实现更加细腻的访问控制。文章还介绍了读写锁的特点和使用方法,以及与互斥锁的异同。最后,强调了正确使用读写锁的重要性,以及解锁未被锁定的锁可能引发的panic。 总的来说,本文内容涵盖了并发编程中的重要概念和同步工具,以及互斥锁和读写锁的使用方法和注意事项,对于读者快速了解并发编程中的同步工具和锁的特点具有很高的参考价值。
《Go 语言核心 36 讲》,新⼈⾸单¥59
全部留言(49)
- 最新
- 精选
- Geek_cd5dcf讲的通俗易懂,还是挺好理解的,想问下mutex如果加锁后 mutex.lock() defer mutex.unlock() 在所有场景下都不会出错吗?
作者回复: 对,不会,这是defer的机制保证的。
2018-10-11621 - 🐻1. Locker 接口 2. func (rw *RWMutex) RLocker() Locker
作者回复: √
2019-03-1616 - Pana如果cpu只有一个核心,是不是就不会产生并发的情况?
作者回复: 如果CPU只有一个核心,那么就不会有真正的并行计算了。但是,并发还是会有的。这是因为仍然可以同时有多个goroutine存在(它们可以同时处于可运行状态),只不过Go语言的运行时系统无法让它们在同一时刻都运行罢了。 并发和并行这两个词的含义是不同的,需要我们分清楚。简单来说,并发是指在同一个时间段内提交多个任务给系统,并行是指在同一时刻系统能执行多个任务。
2020-03-08313 - 授人以🐟,不如授人以渔老师,麻烦分析一下这样的场景:main goroutine 拿到读锁,此时 goroutine 1 试图拿到写锁但被阻塞,紧接着 goroutine 2 试图拿到读锁。我想知道 goroutine 2 为什么也会被阻塞,另外 main goroutine 读锁被释放后,哪个 goroutine 会继续运行?
作者回复: 1. 我又看了下源码。这是为了避免“迭代读锁定”的问题。这个问题最终会导致当前读写锁永不可用。你想想,如果一个 goroutine 一直在不断地读锁定同一个读写锁,那么想要写锁定这个读写锁的 goroutine 就会永远阻塞在那里。 2. main goroutine 释放读锁之后,goroutine 1 会首先得到写锁定的机会。这同样是为了避免“迭代读锁定”。因为如果先给 goroutine 2 机会,那 goroutine 1 的写锁定不是还得等吗?要真是这样的话,假如 main goroutine 和 goroutine 1 都在不断地试图读锁定,那么 goroutine 2 就会一直阻塞下去。 所以说,如果这个事情让你的程序停滞了,那么你就要检查一下程序中是不是有“迭代读锁定”的情况。
2021-05-0747 - 安排goroutine和协程有什么本质区别啊,搜了网上也没看出来啥本质区别,有这方面的资料吗?
作者回复: 传统的协程只是线程内的流程控制工具。它没法做到一个线程内有两个及以上控制流同时进行,只能是这一个挂起那一个运行然后那一个挂起这一个再运行。同时它也不属于多线程编程,没法统一调度多个线程内的控制流。 goroutine 我就不用多说了,它属于用户级线程,与系统级线程搭配使用,很强大也很灵活。深入的东西可以看我的那本《Go 并发编程实战》。
2019-09-2236 - NoTryNoSuccess请问老师,多核心条件下如果两个goroutine底层同时运行在两个线程上,那么此时这两个goroutine实际上是完全并行的。此时它们如果同时进行互斥锁的锁定操作(随后可能同时对同一资源进行写操作)岂不是不能达到对临界区的保护目的了吗?
作者回复: 互斥锁、条件变量和原子操作都是由操作系统和CPU指令集支撑的,所以Go语言的这些同步工具是可以在多线程以及多核CPU甚至多CPU上正确执行的。无需担心。相反,这些同步工具恰恰针对的就是并发和并行的应用场景。这正是它们的用武之地啊。
2020-04-0434 - CcczzZ老师,有个疑问,文中说的这句:「对读锁进行解锁,只会在没有其他读锁锁定的前提下,唤醒“因试图锁定写锁,而被阻塞的 goroutine”」。 我的理解是,对读锁进行解锁时,此刻若存在其他读锁等待的话,是会优先唤醒读锁的,如果不存在其他等待的读锁,才会唤醒写锁。不知道这样理解是否正确? 而基于上面的理解,我写了段代码测试了一下,发现结果并不是这样,实际情况是:「当读锁进行解锁时,若此刻存在其他的读锁和写锁,会根据他们实际阻塞等待的时间长短,优先唤醒并执行」 就像下面,写锁在前面执行,等待的时间也比读锁场,所以当读锁解锁时,优先唤醒的是等待时间较长的写锁。 func main() { var rwMu sync.RWMutex // 模拟多个写/读锁进行阻塞,当释放读锁的时候看谁先获取到锁(会在没有其他读锁的时候,唤醒写锁) rwMu.RLock() fmt.Println("start RLock") // 写 go func() { defer func() { rwMu.Unlock() fmt.Println("get UnLock") }() rwMu.Lock() fmt.Println("get Lock") }() time.Sleep(time.Millisecond * 200) // 读 go func() { defer func() { rwMu.RUnlock() fmt.Println("get RUnLock") }() rwMu.RLock() fmt.Println("get RLock") }() time.Sleep(time.Millisecond * 200) rwMu.RUnlock() fmt.Println("start RUnLock") time.Sleep(time.Second * 1) } 运行结果(等待时间较长的写操作先执行了): start RLock start RUnLock get Lock get UnLock get RLock get RUnLock
作者回复: 简单一句话:读写锁中的读锁锁定操作之间是不互斥的。另外,对于读写锁,读锁锁定操作会与写锁锁定操作互斥,写锁锁定操作之间也会互斥。
2020-01-1634 - 大王叫我来巡山需要请教老师的是,主协程收到信号就被唤醒了,认为可以读了,但是被阻塞的写协程收到锁释放的消息会不会比主协程要早,然后继续获得写的机会,主协程会不会被阻塞?我认为是不会的,此处的锁只是保证了不同写协程互斥的写入,也就是写操作是原子的,但是并不保证读操作一定在写完后就读吧
作者回复: 对于非缓冲通道,写的 goroutine 必然会先完成操作。锁本身只保证互斥。被阻塞的 goroutine 也会有先有后,但会根据被阻塞那一刻的先后,而不是什么读写的先后。 另外互斥锁跟原子操作有本质上的区别,不要搞混。 再另外,goroutine 与协程也有本质上的区别,不要搞混。
2019-09-1323 - 芝士老爹如果一直有新的读锁请求,会不会导致写锁锁不了? 还是说如果有了一个wlock锁请求了,现在因为有rlock未释放锁,wlock的协程被阻塞,后面再有新的rlock锁请求也会先被阻塞,等待wlock锁协程先恢复?
作者回复: 那要看谁先等待了,这里的等待队列是先进先出的。
2019-08-0433 - soooldier配套代码里puzzlers/article26下并没有demo58.go,也没有demo59.go,懵圈中。。。
作者回复: 看这里吧:https://github.com/hyper0x/Golang_Puzzlers/tree/master/src/puzzlers/article22 你可以把这里的 article 理解成 topic。一些比较长的 topic 可能会被编辑拆分为多篇文章,所以就出现了这种情况。太长的文章对读者们不太友好,不容易集中精力读下去。 你可以对照着专栏的目录,按照主题,找一下对应的 articleXX 目录。 Update: 我刚刚添加了一个序号映射表:https://github.com/hyper0x/Golang_Puzzlers/blob/master/mapping_table.md 。你用这个就可以方便地对照了。
2019-05-312