美文网首页
C++并发编程2——为共享数据加锁(四)

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

作者: 小熊猜猜我有几颗糖 | 来源:发表于2016-09-29 08:35 被阅读667次

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

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

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

    为了防止共享数据初始化时数据被破坏,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>可以帮我们处理这些问题。

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

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

    关注微信公众号

    相关文章

      网友评论

          本文标题:C++并发编程2——为共享数据加锁(四)

          本文链接:https://www.haomeiwen.com/subject/ukpkyttx.html