26 | sync.Mutex与sync.RWMutex
26 | sync.Mutex与sync.RWMutex
讲述:黄洲君
时长16:29大小7.54M
前导内容: 竞态条件、临界区与同步工具
问题解析
知识扩展
总结
思考题
赞 16
提建议
精选留言(45)
- 属雨2018-10-10第一个问题: Lock接口。 第二个问题: 变量.Rlock()共 2 条评论31
- 阳仔2019-04-03学习将近一半的课程了发现: 1. 内容不够简洁,很多知识点其实画图出来更容易让读者理解 2. 感觉不大适合初学者,反而对已经入门有一定经验的学习者会帮助更大一些 3. 可以看出来作者是非常精通go的共 4 条评论28
- Geek_cd5dcf2018-10-11讲的通俗易懂,还是挺好理解的,想问下mutex如果加锁后 mutex.lock() defer mutex.unlock() 在所有场景下都不会出错吗?
作者回复: 对,不会,这是defer的机制保证的。
共 6 条评论19 - 🐻2019-03-161. Locker 接口 2. func (rw *RWMutex) RLocker() Locker
作者回复: √
14 - Pana2020-03-08如果cpu只有一个核心,是不是就不会产生并发的情况?
作者回复: 如果CPU只有一个核心,那么就不会有真正的并行计算了。但是,并发还是会有的。这是因为仍然可以同时有多个goroutine存在(它们可以同时处于可运行状态),只不过Go语言的运行时系统无法让它们在同一时刻都运行罢了。 并发和并行这两个词的含义是不同的,需要我们分清楚。简单来说,并发是指在同一个时间段内提交多个任务给系统,并行是指在同一时刻系统能执行多个任务。
共 2 条评论8 - 安排2019-09-22goroutine和协程有什么本质区别啊,搜了网上也没看出来啥本质区别,有这方面的资料吗?
作者回复: 传统的协程只是线程内的流程控制工具。它没法做到一个线程内有两个及以上控制流同时进行,只能是这一个挂起那一个运行然后那一个挂起这一个再运行。同时它也不属于多线程编程,没法统一调度多个线程内的控制流。 goroutine 我就不用多说了,它属于用户级线程,与系统级线程搭配使用,很强大也很灵活。深入的东西可以看我的那本《Go 并发编程实战》。
共 3 条评论5 - Hector2019-05-16读写锁总结,1、可以随便读,即使加锁多个goroutine同时读 2、加锁写的时候,啥也不能干,不能写。即使没加读锁也不能读。老师写的有点绕蒙了。 引用原文:对写锁进行解锁,会唤醒“所有因试图锁定读锁,而被阻塞的 goroutine”,并且,这通常会使它们都成功完成对读锁的锁定。意思就是在对资源的写锁进行解锁时,原来你在对该资源上写锁的时候,所有的读锁会锁定来配合写操作,直到写锁解除锁定时,这些读锁才会解锁。展开共 1 条评论5
- 授人以🐟,不如授人...2021-05-07老师,麻烦分析一下这样的场景: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 就会一直阻塞下去。 所以说,如果这个事情让你的程序停滞了,那么你就要检查一下程序中是不是有“迭代读锁定”的情况。
共 4 条评论4 - NoTryNoSuccess2020-04-04请问老师,多核心条件下如果两个goroutine底层同时运行在两个线程上,那么此时这两个goroutine实际上是完全并行的。此时它们如果同时进行互斥锁的锁定操作(随后可能同时对同一资源进行写操作)岂不是不能达到对临界区的保护目的了吗?
作者回复: 互斥锁、条件变量和原子操作都是由操作系统和CPU指令集支撑的,所以Go语言的这些同步工具是可以在多线程以及多核CPU甚至多CPU上正确执行的。无需担心。相反,这些同步工具恰恰针对的就是并发和并行的应用场景。这正是它们的用武之地啊。
共 3 条评论4 - sky2018-10-22郝大 关于demo59这个案例能大概描述下具体的功能流程吗 代码看起来没有方向感啊共 1 条评论4
- 传说中的成大大2020-03-31今日总结 今天主要是讲了 关于并发同步问题 两种方式 1. 互斥锁 sync/Mutex 保证他们对同一个临界区的访问是串行的 2. 读写锁 分为读锁和写锁 当读写锁定时 任何尝试的写的锁都会被阻塞 而其他的读却不会 原因在于写的时候会改变内存区域的数据 当写锁锁定时 无论是读还是写加锁都会阻塞 原因同上 当写锁进行解锁时 会唤醒所有读锁阻塞的goroutine 而读锁解锁时 会唤醒某一个写锁 具体是哪一个写锁 就看那个写锁等待的事件最长 尤其要注意 一个锁应该只保护一个临界区域 不要尝试重复锁定(有可能会导致死锁) 引发系统运行时自动的panic 并且这个panic无法被恢复 不要对未加锁的锁进行 会引发panic 并且无法恢复 不要传递锁 因为锁时结构体类型 传递时 是值传递 会产生很多副本 导致 锁不是一个锁了 一定要释放锁 最好的办法还是 defer语句解锁 加锁和解锁语句成对出现 关于思考题 Lock接口 2func (rw *RWMutex) RLocker() Locker展开3
- CcczzZ2020-01-16老师,有个疑问,文中说的这句:「对读锁进行解锁,只会在没有其他读锁锁定的前提下,唤醒“因试图锁定写锁,而被阻塞的 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展开
作者回复: 简单一句话:读写锁中的读锁锁定操作之间是不互斥的。另外,对于读写锁,读锁锁定操作会与写锁锁定操作互斥,写锁锁定操作之间也会互斥。
共 3 条评论3 - Leon📷2018-11-13老师,我想问下多协程并发情况怎么调试日志,google官方似乎也不推荐我们在日志中把协程号打印出来,只能通过添加唯一序列号识别吗共 1 条评论3
- Geek_b5876e2020-01-19通过strace观察了一下锁的系统调用,发现了 futex ,go 语言锁的实现也是依赖于操作系统原语和信号量的2
- 大王叫我来巡山2019-09-13需要请教老师的是,主协程收到信号就被唤醒了,认为可以读了,但是被阻塞的写协程收到锁释放的消息会不会比主协程要早,然后继续获得写的机会,主协程会不会被阻塞?我认为是不会的,此处的锁只是保证了不同写协程互斥的写入,也就是写操作是原子的,但是并不保证读操作一定在写完后就读吧
作者回复: 对于非缓冲通道,写的 goroutine 必然会先完成操作。锁本身只保证互斥。被阻塞的 goroutine 也会有先有后,但会根据被阻塞那一刻的先后,而不是什么读写的先后。 另外互斥锁跟原子操作有本质上的区别,不要搞混。 再另外,goroutine 与协程也有本质上的区别,不要搞混。
共 2 条评论2 - 芝士老爹2019-08-04如果一直有新的读锁请求,会不会导致写锁锁不了? 还是说如果有了一个wlock锁请求了,现在因为有rlock未释放锁,wlock的协程被阻塞,后面再有新的rlock锁请求也会先被阻塞,等待wlock锁协程先恢复?
作者回复: 那要看谁先等待了,这里的等待队列是先进先出的。
共 3 条评论2 - soooldier2019-05-31配套代码里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 。你用这个就可以方便地对照了。
2 - timmy212018-10-15郝老师,之前问了一个是否需要上锁的问题。有一个细节我忘说了,一个写者,一个读者,并且“读者”读取变量时不需要保证返回最新值。这种场景下是否可以不上锁?或者不上锁会有什么问题吗?会panic吗?共 2 条评论2
- 给力2020-03-26对于使用锁有个疑问: type Mutex struct { state int32 sema uint32 } state表示锁的一个状态 sema这个变量没太理清是做什么的?什么场景下使用展开
作者回复: sema 实际上代表着基于内存地址的锁机制。相关的代码在 sync 包的 runtime.go 文件和 runtime 包的 seme.go 文件中。你有兴趣的话可以看一看。
1 - jacke2018-11-07问下老师,读写锁解锁部分有点不明白:写锁解锁的时候如果同时有写锁和读锁在等待,是优先唤醒读锁是把? 这个规定对读锁解锁也适用是把?共 1 条评论1