2KB项目,专业的源码交易网站 帮助 收藏 每日签到

C++11 中的两重反省锁定形式

  • 时间:2019-05-14 16:49 编辑:2KB 来源:2KB.COM 阅读:379
  • 扫一扫,手机访问
  • 分享
摘要:
C++11 英文原文:Double-Checked Locking Is Fixed In C++11

两重反省锁定形式(DCLP)在无锁编程方面是有点儿臭名远扬案例学术研讨的滋味。直到2004年,运用java开发并没有平安的方法来完成它。在c++11之前,运用便捷式c+开发并没有平安的方法来完成它。因为惹起人们存眷的缺陷形式表露在这些言语当中,人们Start写它。一组高调的java凑集在一同开发职员并签订了一项声明,题为:“两重反省锁定坏了”。在2004年斯科特 、梅尔斯和安德烈、亚历山宣布了一篇文章,题为:“c+与两重反省锁定的风险”关于DCLP是甚么?这两篇文章都是巨大的引物,为何呢?在事先看来,这些言语都缺乏以完成它。

在过来。java如今可认为修订内存模子,为thevolatileeyword注入新的语义,使得它尽可然平安完成DCLP.异样地,c+11有一个全新的内存模子和原子库使得林林总总的便捷式DCLP得以完成。c+11反过去启示Mintomic,一个小型藏书楼,我本年早些时分宣布的,这使得它尽量的完成一些较旧的c/c++编译器和DCLP.

在这篇文章中,我将重点存眷c++完成的DCLP.

甚么是两重反省锁定?

假定你有一个类,它完成了有名的Singleton 形式,如今你想让它变得线程平安。明显的一个办法就是经过增加一个锁来包管互斥同享。如许的话,假如有两个线程同时挪用了Singleton::getInstance,将只要此中之一会创立这个单例。

Singleton* Singleton::getInstance() { Lock lock; // scope-based lock, released automatically when the function returns if (m_instance == NULL) {
        m_instance = new Singleton;
    } return m_instance;
}

这是完整正当的办法,可是一旦单例被创立,实践上就不再需求锁了。锁纷歧定慢,可是在高并发的前提下,不具有很好的伸缩性。

两重反省锁定形式防止了在单例曾经存在时分的锁定。不外如Meyers-Alexandrescu的论文所显示的,它其实不容易。在那篇论文中,作者描绘了几个出缺陷的用C++完成DCLP的测验考试,并分析了每种状况为何是不平安的。最初,在第12页,他们给出了一个平安的完成,可是它依靠于非指定的,特定平台的内存樊篱(memory barriers)

(译注:内存樊篱就是一种干涉手腕. 他们能包管处于内存樊篱双方的内存操作知足部分有序)

Singleton* Singleton::getInstance() {
    Singleton* tmp = m_instance; ... // insert memory barrier if (tmp == NULL) {
        Lock lock;
        tmp = m_instance; if (tmp == NULL) {
            tmp = new Singleton; ... // insert memory barrier m_instance = tmp;
        }
    } return tmp;
}

这里,我们可以发明两重反省锁定形式是由此得名的:在单例指针m_instance为NULL的时分,我们仅仅运用了一个锁,这个锁使偶尔拜访到该单例的第一组线程继续下去。而在锁的外部,m_instance被再次反省,如许就只要第一个线程可以创立这个单例了。

这与可运转的完成十分附近。只是在凸起显示的几行遗漏了某种内存樊篱。在作者写这篇论文的时分,还没有弥补此项空缺的笨重的C/C++函数。如今,C++11曾经有了。

用 C++11 取得与开释樊篱

你可以用取得与开释樊篱 平安的完成上述完成,在我之前的文章中我曾经具体的说明过这个主题。不外,为了让代码真实的具有可移植性,你还必需要将m_instance包装成原子类型,而且用抓紧的原子操作(译注:即非原子操作)来操作它。这里给出的是后果代码,获得与开释樊篱部分高亮了。

std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance() {
    Singleton* tmp = m_instance.load(std::memory_order_relaxed); std::atomic_thread_fence(std::memory_order_acquire); if (tmp == nullptr) {
        std::lock_guard<std::mutex> lock(m_mutex);
        tmp = m_instance.load(std::memory_order_relaxed); if (tmp == nullptr) {
            tmp = new Singleton; std::atomic_thread_fence(std::memory_order_release); m_instance.store(tmp, std::memory_order_relaxed);
        }
    } return tmp;
}

即便是在多核系统上,它也能够使人信任的任务,由于内存樊篱在创立单例的线程与厥后任何跳过这个锁的线程之间,创立了一种同步的关系。Singleton::m_instance充任保镳变量,而单例自身的内容充任有效载荷

一切那些出缺陷的DCLP完成都无视了这一点:假如没有同步的关系,将没法包管第一个线程的一切写操作——特殊是,那些在单例结构器中履行的写操作——可以对第二个线程可见,固然m_instance指针自身是可见的!第一个线程具有的锁也对此力所不及,由于第二个线程不用取得任何锁,因而它能并发的运转。

假如你想更深化的了解这些樊篱为何和怎么使得DCLP具有可托赖性,在我之前的文章中有一些布景信息,就像这个博客早前的文章一样。

运用 Mintomic 樊篱

Mintomic 是一个小型的C言语的库,它供给了C++11原子库的一个功用子集,此中包括有获得与开释樊篱,并且它是运转于更老的编译器之上的。Mintomic依靠于如许的假定 ,即C++11的内存模子——特别的是,此中包含惹是生非的存储 ——由于它不被更老的编译器支撑,不外这曾经是我们欠亨过C++11能做到的最好水平了。记着这些工具可是若干年来我们在写多线程C++代码时的情况。惹是生非的存储(Out-of-thin-air stores)已被工夫证实是不盛行的,并且好的编译器也根本上不会这么做。

这里有一个DCLP的完成,就是用Mintomic来获得与开释樊篱的。和后面运用C++11获得和开释樊篱的例子比起来,它根本上是等效的。

mint_atomicPtr_t Singleton::m_instance = { 0 };
mint_mutex_t Singleton::m_mutex;

Singleton* Singleton::getInstance() {
    Singleton* tmp = (Singleton*) mint_load_ptr_relaxed(&m_instance); mint_thread_fence_acquire(); if (tmp == NULL) {
        mint_mutex_lock(&m_mutex);
        tmp = (Singleton*) mint_load_ptr_relaxed(&m_instance); if (tmp == NULL) {
            tmp = new Singleton; mint_thread_fence_release(); mint_store_ptr_relaxed(&m_instance, tmp);
        }
        mint_mutex_unlock(&m_mutex);
    } return tmp;
}

为了完成获得与开释樊篱,Mintomic试图在一切它所支撑的平台上,生成最有效的机械代码。举个例子,这里是Xbox 360上的机械代码后果,Xbox 360是基于PowerPC的。在这个平台上,单行的lwsync是一条最精简的指令,它既可以获得也能够开释樊篱。

假如启用了优化,之前基于C++11的例子也能够(幻想状况下将会)生成完整类似的PowerPC机械代码。惋惜的是,我并没有找来兼容C++11的PowerPC编译器来证实它。

运用c++ 11初级排序束缚

C++11的获得与开释樊篱可以准确的完成DCLP,并且应当可以针对现今大大多数的多核装备,生成优化的机械代码(就像Mintomic做的那样),可是它们仿佛不长短常时兴。在C++11中取得划一后果的首选办法,应当是运用基于初级排序束缚的原子操作。正如我先前所说,一条写开释(write-release)可以同步于一条读获得(read-acquire)。

std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance() { Singleton* tmp = m_instance.load(std::memory_order_acquire); if (tmp == nullptr) {
        std::lock_guard<std::mutex> lock(m_mutex);
        tmp = m_instance.load(std::memory_order_relaxed); if (tmp == nullptr) {
            tmp = new Singleton; m_instance.store(tmp, std::memory_order_release); }
    } return tmp;
}

从技术上说,这类无锁的同步方式,比运用自力樊篱的方式,要不那末严格;上面的操作只是意味着禁止它们本人四周的内存从头排序,这与自力的樊篱分歧,后者意味着禁止一切相邻的操作的特定类型的内存重排序。虽然如斯,在x86/64, ARMv6/v7,和 PowerPC架构上,关于这两种方式,可能的最好代码都是类似的。例如,在一篇早前文章中,我演示了在ARMv7编译器上,C++11初级排序束缚是怎么发送dmb指令的,而这也恰是你在运用自力樊篱时所等待的异样工作。

这两种方式有可能会生成分歧机械代码的一个平台是Itanium。Itanium可使用一条独自的CPU指令,ld.acq来完成C++11的load(memory_order_acquire),并可使用st.rel来完成store(tmp, memory_order_release)。我很想研讨一下这些指令与自力樊篱之间的功能差别,可是我找不到可用的Itanium机械。

另外一个如许的平台是比来呈现的ARMv8架构。ARMv8供给了ldar和stlr指令,除它们也加强了stlr指令和任何后续的ldar指令之间的存储加载排序之外,其它的都与Itanium的ld.acq和st.rel指令很类似。现实上,ARMv8的这些新指令意在完成C++11的SC原子操作,这在前面会讲到。

运用 C++11的次序一致原子

C++11供给了一种完整分歧的办法来写无锁代码。(我们可以以为在某些特定的代码途径上DCLP是“无锁”的,由于并非一切的线程都具有锁。)假如在一切原子库函数上,你疏忽了可选的std::memory_order参数,那末默许值std::memory_order_seq_cst就会将一切的原子变量改变为次序一致的(sequentially consistent) (SC)原子。经过SC原子,只需不存在数据竞争,全部算法就能够包管是次序一致的。SC原子Java 5+中的volatile变量十分类似。

这里是运用SC原子的一个DCLP完成。如之前一切例子一样,一旦单例被创立,第二行高亮将与第一行同步

std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance() { Singleton* tmp = m_instance.load(); if (tmp == nullptr) {
        std::lock_guard<std::mutex> lock(m_mutex);
        tmp = m_instance.load(); if (tmp == nullptr) {
            tmp = new Singleton; m_instance.store(tmp); }
    } return tmp;
}
本文中的一切译文仅用于进修和交换目标,转载请务必注明文章译者、出处、和本文链接。 2KB翻译任务按照 CC 协议,假如我们的任务有进犯到您的权益,请实时联络我们。


2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务

  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【计算机/互联网|】Nginx出现502错误(2020-01-20 21:02)
【计算机/互联网|】网站运营全智能软手V0.1版发布(2020-01-20 12:16)
【计算机/互联网|】淘宝这是怎么了?(2020-01-19 19:15)
【行业动态|】谷歌关闭小米智能摄像头,因为窃听器显示了陌生人家中的照片(2020-01-15 09:42)
【行业动态|】据报道谷歌新闻终止了数字杂志,退还主动订阅(2020-01-15 09:39)
【行业动态|】康佳将OLED电视带到美国与LG和索尼竞争(2020-01-15 09:38)
【行业动态|】2020年最佳AV接收机(2020-01-15 09:35)
【行业动态|】2020年最佳流媒体设备:Roku,Apple TV,Firebar,Chromecast等(2020-01-15 09:31)
【行业动态|】CES 2020预览:更多的流媒体服务和订阅即将到来(2020-01-08 21:41)
【行业动态|】从埃隆·马斯克到杰夫·贝佐斯,这30位人物定义了2010年代(2020-01-01 15:14)
联系我们

Q Q: 7090832

电话:400-0011-990

邮箱:7090832@qq.com

时间:9:00-23:00

联系客服
商家入住 服务咨询 投拆建议 联系客服
0577-67068160
手机版

扫一扫进手机版
返回顶部