C++并发编程2——为共享数据加锁(四)

如果某个想法是你唯一的想法,再也没有比这个更危险的事情了。

本节会阐述保护共享数据的替代方案,很多情况下,使用互斥量并不合适,会带来性能消耗。下文会详细讲解集中通用的场景。

保护共享数据的初始化过程

为了防止共享数据初始化时数据被破坏,C++提供了std::once_flag和std::call_once来保证共享数据初始化的正确性。

// using mutex
std::shared_ptr<some_resource> resource_ptr;
std::mutex resource_mutex;
void foo() 
{
    std::unique_lock<std::mutex> lk(resource_mutex);
    if(!resource_ptr)
    {
        resource_ptr.reset(new some_resource);
    }
    lk.unlock();
    resource_ptr->do_something();
}

// using call_once
std::shared_ptr<some_resource> resource_ptr;
std::once_flag resource_flag;

void int_resource()
{
    resource_ptr.reset(new some_resource);
}

void foo()
{
    std::call_once(resource_flag, init_resource);
    resource_ptr->do_something();
}

保护很少更新的数据量

对于这种共享数据可以采用“读者-写着锁”,其允许两种不同的使用方式:一个作者线程独占访问和共享访问,让多个读者线程并发访问。

C++标准并没有提供相关的解决方案,我们可以使用boost::shared_mutex来做同步。对于更新操作,可以使
std::lock_guard<boost::shared_mutex>std::unique_lock<boost::shared_mutex>进行上锁;对于访问操作,可以使用boost::shared_lock<boost::shared_mutex>获取共享访问权。我们来看下面例子:

#include <map>
#include <string>
#include <mutex>
#include <boost/thread/shared_mutex.hpp>
class dns_entry;
class dns_cache
{
  std::map<std::string,dns_entry> entries;
  mutable boost::shared_mutex entry_mutex;
public:
  dns_entry find_entry(std::string const& domain) const
  {
    boost::shared_lock<boost::shared_mutex> lk(entry_mutex); 
    std::map<std::string,dns_entry>::const_iterator const it=
       entries.find(domain);
    return (it==entries.end())?dns_entry():it->second;
  }
  void update_or_add_entry(std::string const& domain,
                           dns_entry const& dns_details)
  {
    std::lock_guard<boost::shared_mutex> lk(entry_mutex);
    entries[domain]=dns_details;
} };

上面代码,find_entry()使用了boost::shared_lock<>实例来保护器共享和只读权限;update_or_add_entry()使用std::lock_guard<>实例来独占访问权限。

嵌套锁

对于一个已经上锁的互斥量多次上锁,会出现未定义行为。然而对于嵌套锁std::recursive_mutex来说,多次上锁不会出现问题。

在互斥量锁住其他线程前,你必须释放你拥有的所有 锁,所以当你调用lock()三次时,你也必须调用unlock()三次。正确使
用 std::lock_guard<std::recursive_mutex>和 std::unique_lock<std::recursice_mutex>可以帮我们处理这些问题。

大多数情况下,嵌套锁是用在可被多线程并发访问的类上,所以其拥有一个互斥量保护其成员数据。每个公共成员函数 都会对互斥量上锁,然后完成对应的功能,之后再解锁互斥量。不过,有时一个公共成员函 数会调用另一个公共函数作为其操作的一部分。

不过上面提高的方案是不推荐的,推荐的做法是——从中提取出一个函数作为类的私有成员, 并且让所有成员函数都对其进行调用,这个私有成员函数不会对互斥量进行上锁(在调用前必 须获得锁)。


关注微信公众号
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值