读写锁可以分为:公平锁,读优先,写优先,优先级锁等。Linux系统提供了pthread_rwlock系列函数作为读写锁的实现,同样的Boost库提供了share_lock作为读写锁实现的辅助类。C++标准库没有提供读写锁,但是我们可以实用mutex和condition_variable来很容易的实现读写锁。
- boost::shared_lock
- std::unique_lock
- std::lock_guard
- pthread_rwlock_init
- pthread_rwlock_destroy
- pthread_rwlock_rdlock
- pthread_rwlock_wrlock
- pthread_rwlock_unlock
读写锁实现思路
- 公平锁:实用队列来管理所,先到先得
- 读优先:这种场合用于写少读多的情况,只要有读请求则写请求永远等待
- 写优先:这种场合和读优先相反,只要有写请求,则读永远被阻塞
- 优先级锁:带有优先级的锁,优先级高的锁先获取资源,可以使用set管理请求资源的锁,并按照优先级排序
公平锁
其实,标准库提供的mutex就是一种公平锁,因为被唤醒的线程是随机的。如果强调真正意义的公平,则可以使用队列来管理锁,只有处于队列头的锁才能获取资源。其实Linux系统已经实现了公平锁——在pthread_mutex初始化时传入参数mutexattr,其包含如下几种:
- PTHREAD_MUTEX_TIMED_NP ,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
- PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
- PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。
- PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。
因此,只需要在创建mutex的时候指定 PTHREAD_MUTEX_TIMED_NP 属性即可。
读优先/写优先
读优先和写优先的本质都是一样的。在Linux中有线程的读写锁,同样的也可以指定读写锁的属性。
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); attr 共有 3 种选择
- PTHREAD_RWLOCK_PREFER_READER_NP (默认设置) 读者优先,可能会导致写者饥饿情况
- PTHREAD_RWLOCK_PREFER_WRITER_NP 写者优先,目前有 BUG,导致表现行为和 PTHREAD_RWLOCK_PREFER_READER_NP 一致
- PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP 写者优先,但写者不能递归加锁
这里用C++的mutex和condition_variable实现一个写优先的锁。满足以下逻辑:
- 当读锁占用锁的时候,其他线程也可以获取读锁
- 当写锁占用锁的时候,其他任何线程既不可以获取读锁,也不可以获取写锁
- 当有写锁在等待的时候,优先唤醒写锁
class write_priotity_lock
{
public:
void read_lock()
{
std::unique_lock<std::mutex> lock(m_mutex);
m_read_cv.wait(lock,[this](){
return this->m_write_count == 0;
});
m_read_count++;
}
void write_lock()
{
std::unique_lock<std::mutex> lock(m_mutex);
m_write_count++;
m_write_cv.wait(lock,[this](){
return this->m_write_count <= 1 && this->m_read_count == 0;
});
}
void read_release()
{
--m_read_count;
if(m_read_count == 0 && m_write_count > 0)
{
m_write_cv.notify_one();
}
}
void write_release()
{
--m_write_count;
if(m_write_count >= 1)
{
m_write_cv.notify_one();
}
else
{
m_read_cv.notify_all();
}
}
private:
std::condition_variable m_write_cv;
std::condition_variable m_read_cv;
std::atomic<int32_t> m_read_count;
std::atomic<int32_t> m_write_count;
std::mutex m_mutex;
};
文档信息
- 本文作者:will kall
- 本文链接:https://fishlovee.github.io/2020/07/03/11-%E8%AF%BB%E5%86%99%E9%94%81%E7%9A%84C++11%E5%AE%9E%E7%8E%B0/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)