读写锁 read_write_mutex
对于一个数据操作,简单的分可以分为读和写。
但是经常会遇到多人同时访问一个数据的情况:
- 多人读
- 多人写
- 有人读,有人写
处理这种情况,常用的方式是加锁(Mutex
):
- 读与读之间,允许并发。
- 读与写,写与写之间互斥。
所以读写锁需要支持以上两种情况。
读写锁
// file: read_write_mutex.h
#pragma once
#include <mutex>
#include <condition_variable>
namespace zone
{
class read_write_mutex
{
public:
read_write_mutex() = default;
~read_write_mutex() = default;
read_write_mutex(const read_write_mutex &) = delete;
read_write_mutex & operator=(const read_write_mutex &) = delete;
read_write_mutex(const read_write_mutex &&) = delete;
read_write_mutex & operator=(const read_write_mutex &&) = delete;
void lock_read() {
std::unique_lock<std::mutex> lock( m_mutex );
m_cond_read.wait(lock, [this]()-> bool {
return m_write_count == 0;
});
++m_read_count;
}
void unlock_read() {
std::unique_lock<std::mutex> lock( m_mutex );
if (--m_read_count == 0 && m_write_count > 0) {
m_cond_write.notify_one();
}
}
void lock_write() {
std::unique_lock<std::mutex> lock( m_mutex );
++m_write_count;
m_cond_write.wait(lock, [this]()-> bool {
return m_read_count == 0 && !m_writing;
});
m_writing = true;
}
void unlock_write() {
std::unique_lock<std::mutex> lock( m_mutex );
if (--m_write_count == 0) {
m_cond_read.notify_all();
} else {
m_cond_write.notify_one();
}
m_writing = false;
}
private:
volatile size_t m_read_count = 0;
volatile size_t m_write_count = 0;
volatile bool m_writing = false;
std::mutex m_mutex;
std::condition_variable m_cond_read;
std::condition_variable m_cond_write;
};
template<typename _ReadWriteLock>
class unique_read_lock
{
public:
explicit unique_read_lock(_ReadWriteLock & rwLock)
: m_ptr_rw_lock(&rwLock) {
m_ptr_rw_lock->lock_read();
}
~unique_read_lock() {
if (m_ptr_rw_lock) {
m_ptr_rw_lock->unlock_read();
}
}
unique_read_lock() = delete;
unique_read_lock(const unique_read_lock &) = delete;
unique_read_lock & operator = (const unique_read_lock &) = delete;
unique_read_lock(const unique_read_lock &&) = delete;
unique_read_lock & operator = (const unique_read_lock &&) = delete;
private:
_ReadWriteLock * m_ptr_rw_lock = nullptr;
};
template<typename _ReadWriteLock>
class unique_write_lock
{
public:
explicit unique_write_lock(_ReadWriteLock & rwLock)
: m_ptr_rw_lock(&rwLock) {
m_ptr_rw_lock->lock_write();
}
~unique_write_lock() {
if (m_ptr_rw_lock) {
m_ptr_rw_lock->unlock_write();
}
}
unique_write_lock() = delete;
unique_write_lock(const unique_write_lock &) = delete;
unique_write_lock & operator = (const unique_write_lock &) = delete;
unique_write_lock(const unique_write_lock &&) = delete;
unique_write_lock & operator = (const unique_write_lock &&) = delete;
private:
_ReadWriteLock * m_ptr_rw_lock = nullptr;
};
}
通过 m_read_count
, m_write_count
和 m_writing
记录当前的读写状态(读等待数,写等待数,当前是否正在写入)。
当状态切换时唤醒对应的 m_cond_read
和 m_cond_write
等待,进行读写切换。
接口使用方法:
// 引入读写锁头文件
#include "read_write_mutex.h"
// 定义读写锁
zone::read_write_mutex rwmutex;
void read_function() {
// 对该读操作,加读锁
zone::unique_read_lock<zone::read_write_mutex> lock( rwmutex );
// 读操作
}
void write_function() {
// 对该写操作,加写锁
zone::unique_write_lock<zone::read_write_mutex> lock( rwmutex );
// 写操作
}
测试
以下程序开启输入线程(threadWrite
)和打印线程(threadRead
)。
输入线程读取 10 次命令行输入后关闭。
打印线程每隔 10 ms 读取关联的 msg
是否有变化,发生变化时打印出来。直到 end == true
时结束。
#include "read_write_mutex.h"
#include <iostream>
#include <thread>
#include <chrono>
#include <string>
#include <future>
#include <atomic>
void readData(zone::read_write_mutex& rwmutex, std::string& msg, int& lastLen) {
zone::unique_read_lock<zone::read_write_mutex> readLock( rwmutex );
if (lastLen != msg.length()) {
lastLen = msg.length();
std::cout << ">>> [ " << lastLen << " ] " << msg << std::endl;
}
}
void writeData(zone::read_write_mutex& rwmutex, std::string& msg, std::string in) {
zone::unique_write_lock<zone::read_write_mutex> writeLock( rwmutex );
msg += '_' + in;
}
void threadRead(zone::read_write_mutex& rwmutex, std::string& msg, std::atomic<bool>& end) {
int len = 0;
while(!end) {
readData(rwmutex, msg, len);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void threadWrite() {
std::string msg = "Hello";
zone::read_write_mutex rwmutex;
std::atomic<bool> end(false);
std::thread th_read(threadRead, std::ref(rwmutex), std::ref(msg), std::ref(end));
int n = 10;
for (int i = 0; i < n; ++ i) {
std::string newMsg;
std::cout << std::endl << "[ " << i+1 << '/' << n << " ] : ";
std::cin >> newMsg;
writeData(rwmutex, msg, newMsg);
}
end = true;
th_read.join();
}
int main() {
std::thread th_write(threadWrite);
th_write.join();
return 0;
}
测试结果:
# g++ -std=c++11 test_rwlock.cpp -lpthread && ./a.out
[ 1/10 ] : >>> [ 5 ] Hello
test
[ 2/10 ] : >>> [ 10 ] Hello_test
zone
[ 3/10 ] : >>> [ 15 ] Hello_test_zone
abc
[ 4/10 ] : >>> [ 19 ] Hello_test_zone_abc
xyz
[ 5/10 ] : >>> [ 23 ] Hello_test_zone_abc_xyz
123
[ 6/10 ] : >>> [ 27 ] Hello_test_zone_abc_xyz_123
AAA
[ 7/10 ] : >>> [ 31 ] Hello_test_zone_abc_xyz_123_AAA
micro
[ 8/10 ] : >>> [ 37 ] Hello_test_zone_abc_xyz_123_AAA_micro
end
[ 9/10 ] : >>> [ 41 ] Hello_test_zone_abc_xyz_123_AAA_micro_end
byb
[ 10/10 ] : >>> [ 45 ] Hello_test_zone_abc_xyz_123_AAA_micro_end_byb
bye
>>> [ 49 ] Hello_test_zone_abc_xyz_123_AAA_micro_end_byb_bye
来源:https://blog.csdn.net/wilson1068/article/details/100766622