保护共享数据的替代措施
互斥量是保护数据的一种通用错失,但并不是唯一错失,这里有很多的替代方法可以在一些特定的情况下,提供更加合适的保护
- 共享数据初始化过程的保护
假设对于一个共享源,构造的代价非常昂贵,在单线程中,通常采用延迟初始化的方法例子如下;shard_ptr<resource> resource_ptr; void foo(){ if(resource_ptr == nullptr){ resource_ptr.reset(new resource); } resource_ptr->dowork(); }
上述代码在单线程中是能够稳定运行的,但是在多线程中就会存在一定的问题,因此,在多线程中,会对resource_ptr加锁保护,代码如下
shared_ptr<resource>resource_ptr; mutex m; void foo(){ lock_guard<mutex>loc(m); if(resource_ptr == nullptr){ resource_ptr.reset(new resource); } loc.unlock(); resource_ptr->dowork(); }
上述代码虽然解决了线程安全的问题,但是,由于resource_ptr只需要初始化一次,但后面每次调用foo的时候,都需要加锁检查resource_ptr(虽然我们已经知道肯定已经进行了初始化)。
人们对这段代码进行了各种改造,包括声名狼藉的双检查锁(为什么说他声名狼藉,具体原因在此不多加说明,感兴趣的读者可以查看网上的说法)
- C++的保护方法
在C++中提供了std::once_flag
和std::call_once
来处理这样的情况,比起锁住互斥量,显示检查指针初始化来说,每个线程只需要使用call_once,在这个函数结束的时候,就能安全地知道指针是否已经被其他线程初始化了,消耗明显更少,具体操作如下所示;shared_ptr<resource>resource_ptr; once_flag resource_flag; void init(){ resouce_ptr.reset(new resource); } void foo(){ call_once(resource_flag,init); resource_ptr->dowork(); }
当作为类成员的时候,需要传入this指针具体如下;
class A{ shared_ptr<m>ptr; once_flag ptr_flag; void init(){ ptr.reset(new m); } void foo(){ call_once(ptr_flag,init,this); ptr->dowork()'' } }
类似于thread和bind的构造方法,注意一点,mutex和once_flag实力都是不能拷贝或者移动的
- 保护很少更新的数据结构
这里需要用到另外一种锁,叫读者-作者锁,它允许两种不同的使用方法,一个作者线程独占访问和共享访问,让多个读者线程并发访问由于c++标砖中没有这样的方法,但是boost库中提供了这样的方法,示例代码如下所示:class A; class A{ mutable boost::shared_mutex sm; std::mutex m; void read(){ boost::shared_lock<boost::shared mutex>sl(sm); ... } vodi write(){ std::lock_guard<mutex>loc(m); ... } }
原文链接: https://www.cnblogs.com/hhyandcpp/p/17040624.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/311039
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!