[原创]单线程与多线程map表操作操作差异

lajabs 2011-04-15 05:24:20
多线程具有良好的并发处理能力,以及多核处理器的良好利用,
单线程程序运行期间由于没有线程切换,所以在单处理环境下有着高性能,相比较多线程服务在共享资源方面的操作可以避免锁操作及相关拷贝,本文使用一个map表说明差异,假设key为int类型,而值为string,那么单线程版本比较简单,代码如下:



boost::unordered_map<int,string> map;
....
string *Get(int key)
{
MapInt::iterator it = m_map_int.find(key); //查找迭代器
return (it == m_map_int.end()) ? NULL : &it->second; //值判断
}



而多线程版本很多人马上想到的可能是加锁,那么实现想当然有可能就是这样:



boost::unordered_map<int,string> map;
boost::shared_mutex <m_mutex>; //锁定义
....
string *Get(int key)
{
boost::shared_lock lock( m_mutex ); //这边定义为读锁
MapInt::iterator it = m_map_int.find(key);
return (it == m_map_int.end()) ? NULL : &it->second;
}



但是这样仍然有问题,上面的函数返回了一个string的指针,这个指针在外部调用map表中的值,
这是非线程安全的,换句话说这个锁定义是无效的,多线程中的标准做法如下:



boost::unordered_map<int,string> map;
boost::shared_mutex <m_mutex>; //锁定义
....
string Get(int key)
{
boost::shared_lock lock( m_mutex ); //这边定义为读锁
MapInt::iterator it = m_map_int.find(key);
return (it == m_map_int.end()) ? NULL : it->second;
}



改进后的多线程map表的读访问直接返回了一个值的拷贝string对象,这样保证了线程的绝对安全,但又面临另一个问题,值拷贝是低效的,特别当string对象非常庞大时,这个过程将变得十分漫长。
如何解决读锁的问题呢,一种比较可行的办法就是在写入时记录值的hash值(保证写入时的线程安全),或者说是一个校验码,这样在读取时就可以无需加锁,只需判断校验码的正确性即可,简称“读时校验”,示意代码:


struct value
{
string val;
int hash;
};

boost::unordered_map<int,value> map;
....
void Get(int key,string &str)
{
MapInt::iterator it = m_map_int.find(key);
str = it->second.val;
//这里用到"hash"函数得到值的hash值与写入时的hash值做比较,当无法匹配时说明其它线程正在写操作,这时可以适当"等待"后重新读取。
while(hash(str) != it->second.hash)
{
sleep(1);
it = m_map_int.find(key);
str = it->second.val;
}
}



经过上述改进后,性能上有了较大提升,但仍然有可能读取到“非正常”数据,因为hash值是可碰撞的并“欺骗”了校验,虽然这个可能性极低,但在一些对数据安全性要求较高的场合就显得特别谨慎了,所以追求性能的同时也会付出额外的代价。
在一些使用简单锁机制的服务,如Memcached就会有类似的问题,使用时要注意在应用层做好防范工作。

总结,相对单线程,在多线程程序中,需要额外的锁开销(将近500ns)以及很多的拷贝工作,这使得多线程程序在某些情况下要比单线程慢得多(Memcached早期版本这样典型的追求性能的服务中就只使用单线程),所以当我们选择单线程或多线程程序进行开发时,非常有必要考虑这样类似的问题。

blog:http://lajabs.net/?p=267
...全文
342 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
lajabs 2011-04-15
  • 打赏
  • 举报
回复
这个是boost的
quwei197874 2011-04-15
  • 打赏
  • 举报
回复
stl多线程操作很容易出错.

64,281

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧