07|并发安全:如何为不同并发场景选择合适的锁?
徐逸

你好,我是徐逸。
上节课我们一起学习了并发等待技术。不过在实际的编程实践中,我们会遇到各种各样的并发场景,所需要的并发技术也会有所不同。今天咱们就来聊聊在并发环境下,如何巧妙地运用锁,实现高性能、安全地访问多协程共享的数据。
我们先从一个问题入手。假如我们现在需要实现一个底层用 map 类型存储数据的本地缓存,该怎么设计,才能在并发环境下高性能且安全地访问这个缓存呢?
互斥锁
对于多协程共享数据的安全访问,最简单的方案就是用互斥锁。互斥锁能保证在同一时刻,只有一个协程能够访问被保护的共享数据。
在 Golang 中,并发包 sync 里面的 Mutex 类型实现了互斥锁功能。它的核心是下面两个方法。
Lock 方法,用于加锁,当锁已经被占用时,调用协程会阻塞直到锁可用。
Unlock 方法,用于释放锁。
知道了 Mutex 类型的方法,如同下面的代码一样,咱们可以在本地缓存的读写操作中,调用 Mutex 对象的 Lock 和 Unlock 方法来实现并发安全的本地缓存访问。
公开
同步至部落
取消
完成
0/2000
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结

1. 互斥锁和读写锁是在并发编程中常用的锁机制,用于保证共享数据的安全访问。 2. 互斥锁适用于写操作频繁的场景,而读写锁适用于读操作频繁的场景,能够提升共享数据的并发访问性能。 3. 分段锁是针对map结构的并发访问优化方案,通过将大的共享数据结构划分成多个较小的段,每个段有独立的锁,可以减少并发访问时的阻塞情况。 4. 在读多写少的场景中,读写锁的性能明显优于互斥锁,可以提升并发读取的效率。 5. 通过合适选择互斥锁、读写锁或分段锁,可以根据不同的并发场景实现高性能、安全地访问共享数据. 6. atomic包提供了对数据进行原子操作的功能,适用于无锁编程,避免加锁操作,提升性能。 7. atomic包提供了Add、CAS、Load、Store、Swap等操作,用于对整型和指针类型进行原子操作。 8. atomic包还提供了Value类型,用于对复杂类型对象进行原子存取的能力。 9. 选择合适的并发安全访问方式是在并发编程中需要注意的重点,根据不同场景选择合适的锁机制或无锁编程方式。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Go 服务开发高手课》,新⼈⾸单¥59
《Go 服务开发高手课》,新⼈⾸单¥59
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(1)
- 最新
- 精选
- lJ1. LockFreeCache虽然 atomic.Value 可以确保对整个 map 的原子性更新,但好像依然存在读写冲突问题。例如,在 Get 方法中读取了 map 的指针后,对 map 的内容操作是非原子的。如果此时另一个协程调用 Update 替换了整个 map,那么读取就可能是过时的数据了吧。如果再加互斥锁,那么还不如基于RWMutexCache实现呢。另外,调用 Update 方法时,新的 map 被存储在 atomic.Value 中,而旧的 map 仍然在内存中,会导致内存泄漏的吧。 2. 这种无所编程实现的map其对应的benchmark测试,性能如何 3. 面试中遇到过问基于channel实现的并发安全map,这个实现适用的场景是啥,是否有可替换的方法呢? 4. 思考题 type StackNode struct { value interface{} next *StackNode } type LockFreeStack struct { head unsafe.Pointer // 栈顶节点 } func NewLockFreeStack() *LockFreeStack { return &LockFreeStack{} } func (s *LockFreeStack) Push(value interface{}) { newNode := &StackNode{value: value} for { currentHead := (*StackNode)(atomic.LoadPointer(&s.head)) newNode.next = currentHead if atomic.CompareAndSwapPointer(&s.head, unsafe.Pointer(currentHead), unsafe.Pointer(newNode)) { return } } } func (s *LockFreeStack) Pop() (interface{}, bool) { for { currentHead := (*StackNode)(atomic.LoadPointer(&s.head)) if currentHead == nil { return nil, false } if atomic.CompareAndSwapPointer(&s.head, unsafe.Pointer(currentHead), unsafe.Pointer(currentHead.next)) { return currentHead.value, true } } }2024-12-23归属地:江苏
收起评论