公平锁和非公平锁是计算机科学中的两种锁机制,它们主要用于多线程编程,以控制对共享资源的访问。
一、公平锁 (fair lock)
1. 概念
公平锁是一种按照请求顺序授予锁的机制,即先请求锁的线程会先获得锁,后请求锁的线程会后获得锁。这种锁通过维护一个队列来管理等待的线程,确保每个线程都能公平地获取到锁。
2. 优点
- 避免饥饿:所有线程都有机会获得锁,不会出现某些线程长期得不到锁的情况。
- 可预测性:锁的获取是按顺序进行的,具有较好的可预测性。
3. 缺点
- 性能开销:由于需要维护一个队列,公平锁在管理上有一定的性能开销。
- 上下文切换增加:由于公平锁可能需要频繁地切换线程,导致上下文切换的次数增加,影响性能。
二、非公平锁 (unfair lock)
1. 概念
非公平锁是一种不按照请求顺序授予锁的机制,即任何线程都有可能在任何时候获得锁,而不考虑请求顺序。这种锁通常会优先考虑当前已经持有锁的线程,以提高系统的吞吐量。
2. 优点
- 高性能:由于没有队列管理的开销,非公平锁通常性能较高,特别是在高并发场景下。
- 减少上下文切换:非公平锁可以减少线程之间的上下文切换,提升效率。
3. 缺点
- 可能导致饥饿:某些线程可能长时间得不到锁,导致线程饥饿。
- 不可预测性:锁的获取是随机的,具有较低的可预测性。
三、go语言中的实现
go语言中的锁主要通过sync包提供,常用的锁有mutex(互斥锁)和rwmutex(读写互斥锁)。go的sync.mutex默认实现的是一种非公平锁,但也可以实现公平锁。
1. 非公平锁的实现
go标准库中的sync.mutex是非公平锁的实现。它的主要结构和实现方式如下:
type mutex struct {
state int32
sema uint32
}
func (m *mutex) lock() {
// 快速路径:尝试直接获取锁
if atomic.compareandswapint32(&m.state, 0, 1) {
return
}
// 慢速路径:获取不到锁时,调用lockslow方法
m.lockslow()
}
func (m *mutex) unlock() {
// 快速路径:尝试直接释放锁
if atomic.compareandswapint32(&m.state, 1, 0) {
return
}
// 慢速路径:释放锁时,调用unlockslow方法
m.unlockslow()
}
2. 公平锁的实现
go标准库不直接提供公平锁的实现,但我们可以通过其他方式实现公平锁,比如通过条件变量(sync.cond)来维护等待的队列,从而实现公平锁。
type fairmutex struct {
mu sync.mutex
cond *sync.cond
waiting []chan struct{}
}
func newfairmutex() *fairmutex {
fm := &fairmutex{}
fm.cond = sync.newcond(&fm.mu)
return fm
}
func (fm *fairmutex) lock() {
fm.mu.lock()
defer fm.mu.unlock()
ch := make(chan struct{})
fm.waiting = append(fm.waiting, ch)
if len(fm.waiting) > 1 {
<-ch
}
}
func (fm *fairmutex) unlock() {
fm.mu.lock()
defer fm.mu.unlock()
if len(fm.waiting) > 0 {
fm.waiting = fm.waiting[1:]
if len(fm.waiting) > 0 {
close(fm.waiting[0])
}
}
}
四、总结
- 公平锁:按请求顺序授予锁,避免饥饿,维护队列,开销较大。
- 非公平锁:随机授予锁,高性能,可能导致饥饿。
在go语言中,默认提供的是非公平锁。公平锁可以通过自定义实现来满足特定需求。
到此这篇关于go中公平锁和非公平锁的具体使用的文章就介绍到这了,更多相关go 公平锁和非公平锁内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论