对共享资源进行多线程读写操作有很多方法,本文举出两种方法并进行对比。
一:读写锁方法。线程进行读操作时以读的方式加锁,线程进行写操作时用写的方式加锁。
二:另外一种比较新奇的方法是使用shared_ptr+互斥锁。shared_ptr是一种用引用计数实现的智能指针,当计数为零时,它接管的对象会被销毁。利用这一点可以与互斥锁配合使用实现另外一种比读写锁更为高效的方法。方法如下:
1:对于read端,在读之前加互斥锁,把引用计数加1,解除互斥锁(临界区非常小),读完之后计数减1,这样保证在读期间其引用计数大于1,可以防止并发写。
2:对于write端,对整个写函数加互斥锁,如果引用计数为1,这时可以安全修改共享对象,不必担心有人读它;当引用计数大于1时,生成一个新的数据对象(旧对象的深拷贝),并用一个shared_ptr进行保管,然后与原来的shared_ptr进行交换,对新对象进行写操作。这时旧对象只被读函数中的shared_ptr保管,当那些函数都运行完毕时,旧对象的引用计数成为零,就对象被自动销毁。
这种方法的好处就是在读的时候可以去写,从而会使操作时间有所缩短,缺点是写时有可能会拷贝旧数据,这点到不必太多担心。下面的程序验证了写时拷贝数据发生的概率在1%左右,而且整体时间也会比读写锁方法有所缩短。
实验模拟了股票的购买和查询过程,实验采用四线程,两个线程买股票(写),两个线程查询股票(读)。
CustomerData.h。shared_ptr+互斥锁方法
#ifndef CUSTOMERDATA_H_INCLUDED
#define CUSTOMERDATA_H_INCLUDED
#include"MutexLock.h"
#include"MutexLockGuard.h"
#include<boost/shared_ptr.hpp>
#include<boost/noncopyable.hpp>
#include<vector>
#include<map>
#include<iostream>
using namespace std;
typedef struct MyStock{
string stockName;
int itemCnt;
}MyStock;
class CustomerData{
private:
typedef vector<MyStock> StockList;
typedef boost::shared_ptr<StockList> Stoptr;
typedef map<string,StockList> Map;
typedef boost::shared_ptr<Map> Maptr;
Maptr data_;
MutexLock mutex_;
static int CopyTime;
static int WriteTime;
public:
CustomerData():data_(new Map){}
void traverse(){
Maptr tmp;
{
MutexLockGuard lock(mutex_);
tmp=data_;
cout<<"Copy Time="<<CopyTime<<endl;
cout<<"Write Time="<<WriteTime<<endl;
cout<<(double)CopyTime*100/WriteTime<<"%"<<endl;
}
for(Map::iterator p=data_->begin();p!=data_->end();++p){
cout<<"-----------------------------"<<endl;
cout<<"Customer:"<<p->first<<endl;
for(StockList::iterator q=p->second.begin();q!=p->second.end();++q)
cout<<"\t"<<q->stockName<<":"<<q->itemCnt<<endl;
cout<<"-----------------------------"<<endl;
}
}
int queryCnt(const string& customer,const string& stock){
Maptr tmp;
{
MutexLockGuard lock(mutex_);
tmp=data_;
}
Map::iterator p=tmp->find(cust