读写锁是计算机程序的并发控制的一种同步机制,也称“共享-互斥锁”、多读者-单写者锁。多读者锁,“push lock”) 用于解决。读操作可并发重入,写操作是互斥的。这意味着多个线程可以同时读数据,但写数据时需要获得一个独占的锁。当写者写数据时,其它写者或读者需要等待,直到这个写者完成写操作。读写锁常见的用法是控制线程对内存中的某种数据结构的访问,这种数据结构不能被原子性地更新,并且在完成更新之前都是无效的。
读写锁通常用互斥锁、条件变量、信号量实现。
某些读写锁允许在读模式与写模式之间升降级。[http://www.boost.org/doc/html/thread/synchronization.html#thread.synchronization.mutex_concepts.upgrade_lockable]
优先级策略
读写锁可以有不同的操作模式优先级:
- 读操作优先锁:提供了最大并发性,但在锁竞争比较激烈的情况下,可能会导致写操作饥饿。这是由于只要还有一个读线程持锁,写线程就拿不到锁。多个读者可以立刻拿到锁,这意味着一个写者可能一直在等锁,期间新的读者一直可以拿到锁。极端情况下,写者线程可能会一直等锁,直到所有一开始就拿到锁的读者释放锁。读者的可以是弱优先级的,如前文所述,也可以是强优先级的,即只要写者释放锁,任何等待的读者总能先拿到。
- 写操作优先锁:如果队列中有写者在等锁,则阻止任何新读者拿锁,来避免了写操作饥饿的问题。一旦所有已经开始的读操作完成,等待的写操作立即获得锁。和读操作优先锁相比,写操作优先锁的不足在于在写者存在的情况下并发度低。内部实现需要两把互斥锁。
- 未指定优先级锁:不提供任何读/写的优先级保证。
实现
使用两把互斥锁
使用两把互斥锁与一个整数计数器实现。计数器跟踪被阻塞的读线程。互斥锁保护,供读者使用。互斥锁 (指"global")确保写操作互斥。伪代码:
Begin Read
- Lock .
- Increment .
- If , lock .
- Unlock .
End Read
- Lock .
- Decrement .
- If , unlock .
- Unlock .
Begin Write
- Lock .
End Write
- Unlock .
实现是读操作优先。
使用条件变量与互斥锁
可使用条件变量与普通的互斥锁、整型计数器(表示正在读的个数)与布尔标志(表示正在写)来实现读写锁。
lock-for-read操作:
- Lock (blocking).
- While :
**
- Increment .
- Unlock .
lock-for-write操作:
- Lock (blocking).
- While ( or ):
**
- Set to .
- Unlock .
lock-for-read与lock-for-write各自有自己的逆操作。unlock-for-read通过减量并在变为0时通知。unlock-for-write设置为false并通知。
程序语言支持
- POSIX标准的pthread_rwlock_t与相关操作
- ReadWriteLock 接口,ReentrantReadWriteLock
- std::shared_mutex read/write锁在C++17
- boost::shared_mutex与boost::upgrade_mutex锁在Boost C++ Libraries
- sync.RWMutex在Go语言
- Phase fair reader–writer lock
- std::sync::RwLock,在Rust语言
- Poco::RWLock,在
- mse::recursive_shared_timed_mutex在[//github.com/duneroadrunner/SaferCPlusPlus SaferCPlusPlus]库,是支持[http://en.cppreference.com/w/cpp/thread/recursive_mutex std::recursive_mutex]的recursive ownership语义的[http://en.cppreference.com/w/cpp/thread/shared_timed_mutex std::shared_timed_mutex]的一个实现
- txrwlock.ReadersWriterDeferredLock,用于Twisted
Windows操作系统
SRWLock,见Windows操作系统API,从Windows Vista开始.
读写锁(Slim reader/writer,SRW, lock)用于进程内的线程间同步。 SRW既不是公平的也不是先进先出的。读写锁数据结构的内部实现就是一个指针。读写锁的性能较临界区有很大的提升,这是因为读写锁是基于原子操作,关键段是基于event内核对象的,从用户模式到内核模式的切换占用了大量的时钟周期。相关API:
*InitializeSRWLock:动态初始化SRW结构。也可以直接赋值SRWLOCK_INIT常量来静态初始化SRW结构。不需要显式析构。
*AcquireSRWLockShared:获取共享读的SRW锁。
*AcquireSRWLockExclusive:获取专享写的SRW锁。
*TryAcquireSRWLockShared:试图获取共享读的SRW锁。
*TryAcquireSRWLockExclusive:试图获取专享写的SRW锁。
*ReleaseSRWLockShared:释放已经获取的共享读的SRW锁。
*ReleaseSRWLockExclusive:释放已经获取的专享写的SRW锁。
*SleepConditionVariableSRW:在已经获取SRW锁的前提下,原子操作:在指定条件变量上睡眠并释放SRW锁
可选
read-copy-update (RCU)算法是读写锁的一种替代实现。RCU对读操作是无等待。Linux内核实现了很少写操作的一种RCU叫做seqlock。
参见
- 信号量
- 互斥锁
- 调度 (计算机)
*
- 文件锁定
- 锁 (计算机科学)
注释
参考文献
评论 (0)