使用Mutex替换读写锁
在读《Linux多线程服务端编程》中第二章末尾的时候看到陈硕老师的分析,感觉写的很好,特此进行记录来加深印象
下面的链接🔗是我看过的很好的博客
一个非常好的博客
背景:
多线程C++程序,24h * 5.5d运行。有多个工作线程处理客户的交易请求,有一个Background线程更新内部的数据,线程跟哈希表进行交互。工作线程只读,背景线程进行读写
要求:
这个系统对于读的要求比较敏感,而对于更新(背景线程)的要求不那么敏感,并且一天中更新的频率是很低的。下面使用
mutex
+shared_ptr
降低延迟
主要的思想:
- 如果你是数据的唯一拥有者,那么你可以直接进行修改数据
- 如果你不是数据的唯一拥有者,那么你可以拷贝之后再进行修改
#include <map>
#include <string>
#include <vector>
#include <boost/shared_ptr.hpp>
#include "../Mutex.h"
using std::string;
class CustomerData : boost::noncopyable
{
public:
CustomerData()
: data_(new Map)
{ }
int query(const string& customer, const string& stock) const;
private:
typedef std::pair<string, int> Entry;
typedef std::vector<Entry> EntryList;
typedef std::map<string, EntryList> Map;
typedef boost::shared_ptr<Map> MapPtr;
void update(const string& customer, const EntryList& entries);
void update(const string& message);
static int findEntry(const EntryList& entries, const string& stock);
static MapPtr parseData(const string& message);
MapPtr getData() const
{
muduo::MutexLockGuard lock(mutex_);
return data_;
}
mutable muduo::MutexLock mutex_;
MapPtr data_;
};
int CustomerData::query(const string& customer, const string& stock) const
{
MapPtr data = getData();
Map::const_iterator entries = data->find(customer);
if (entries != data->end())
return findEntry(entries->second, stock);
else
return -1;
}
void CustomerData::update(const string& customer, const EntryList& entries)
{
muduo::MutexLockGuard lock(mutex_);
if (!data_.unique())
{
MapPtr newData(new Map(*data_));
data_.swap(newData);
}
assert(data_.unique());
(*data_)[customer] = entries;
}
void CustomerData::update(const string& message)
{
MapPtr newData = parseData(message);
if (newData)
{
muduo::MutexLockGuard lock(mutex_);
data_.swap(newData);
}
}
int main()
{
CustomerData data;
}
Read
在本样例中体现的就是函数
query
int CustomerData::query(const string& customer, const string& stock) const
{
MapPtr data = getData();
Map::const_iterator entries = data->find(customer);
if (entries != data->end())
return findEntry(entries->second, stock);
else
return -1;
}
对于哈希表来说(这个操作只是读,并没有修改这个map的内容),因此我们复制一下map, 用局部变量
MapPtr data
持有map, 防止并发修改注意我们并没有进行互斥手段,其实互斥手段在
getData()
中,拿到shared_ptr
,之后就不需要访问原有的智能指针了
Write
关键就是看
update
, 更新数据那么要加锁,并且是 **全程加锁 **。
- 如果这时候其他线程在读取,那么不能修改原来的部分,需要复制一个副本进行修改,然后替换
- 如果没有用户在读取,所以是写线程单独持有,直接进行修改就好了
void CustomerData::update(const string& customer, const EntryList& entries)
{
muduo::MutexLockGuard lock(mutex_);
if (!data_.unique())
{
MapPtr newData(new Map(*data_));
data_.swap(newData);
}
assert(data_.unique());
(*data_)[customer] = entries;
}