国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

C++11 中的雙重檢查鎖定模式

開發 后端 前端
在過去。java現在可以為修訂內存模型,為thevolatileeyword注入新的語義,使得它盡可然安全實現DCLP.同樣地,c+11有一個全 新的內存模型和原子庫使得各種各樣的便捷式DCLP得以實現。c+11反過來啟發Mintomic,一個小型圖書館,我今年早些時候發布的,這使得它盡可 能的實現一些較舊的c/c++編譯器以及DCLP.

雙重檢查鎖定模式(DCLP)在無鎖編程方面是有點兒臭名昭著案例學術研究的味道。直到2004年,使用java開發并沒有安全的方式來實現它。在 c++11之前,使用便捷式c+開發并沒有安全的方式來實現它。由于引起人們關注的缺點模式暴露在這些語言之中,人們開始寫它。一組高調的java聚集在 一起開發人員并簽署了一項聲明,題為:“雙重檢查鎖定壞了”。在2004年斯科特 、梅爾斯和安德烈、亞歷山發表了一篇文章,題為:“c+與雙重檢查鎖定 的危險”對于DCLP是什么?這兩篇文章都是偉大的引物,為什么呢?在當時看來,這些語言都不足以實現它。

在過去。java現在可以為修訂內存模型,為thevolatileeyword注入新的語義,使得它盡可然安全實現DCLP.同樣地,c+11有一個全 新的內存模型和原子庫使得各種各樣的便捷式DCLP得以實現。c+11反過來啟發Mintomic,一個小型圖書館,我今年早些時候發布的,這使得它盡可 能的實現一些較舊的c/c++編譯器以及DCLP.

在這篇文章中,我將重點關注c++實現的DCLP.

什么是雙重檢查鎖定?

假設你有一個類,它實現了著名的Singleton 模式,現在你想讓它變得線程安全。顯然的一個方法就是通過增加一個鎖來保證互斥共享。這樣的話,如果有兩個線程同時調用了Singleton::getInstance,將只有其中之一會創建這個單例。

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

這是完全合法的方法,但是一旦單例被創建,實際上就不再需要鎖了。鎖不一定慢,但是在高并發的條件下,不具有很好的伸縮性。

雙重檢查鎖定模式避免了在單例已經存在時候的鎖定。不過如Meyers-Alexandrescu的論文所顯示的,它并不簡單。在那篇論文中,作者描述了幾個有缺陷的用C++實現DCLP的嘗試,并剖析了每種情況為什么是不安全的。最后,在第12頁,他們給出了一個安全的實現,但是它依賴于非指定的,特定平臺的內存屏障(memory barriers)。

(譯注:內存屏障就是一種干預手段. 他們能保證處于內存屏障兩邊的內存操作滿足部分有序)

  1. Singleton* Singleton::getInstance() {  
  2.     Singleton* tmp = m_instance; ... // insert memory barrier if (tmp == NULL) {  
  3.         Lock lock;  
  4.         tmp = m_instance; if (tmp == NULL) {  
  5.             tmp = new Singleton; ... // insert memory barrier m_instance = tmp;  
  6.         }  
  7.     } return tmp;  
  8. }  

這里,我們可以發現雙重檢查鎖定模式是由此得名的:在單例指針m_instance為NULL的時候,我們僅僅使用了一個鎖,這個鎖使偶然訪問到該單例的第一組線程繼續下去。而在鎖的內部,m_instance被再次檢查,這樣就只有第一個線程可以創建這個單例了。

這與可運行的實現非常相近。只是在突出顯示的幾行漏掉了某種內存屏障。在作者寫這篇論文的時候,還沒有填補此項空白的輕便的C/C++函數。現在,C++11已經有了。

用 C++11 獲得與釋放屏障

你可以用獲得與釋放屏障 安全的完成上述實現,在我以前的文章中我已經詳細的解釋過這個主題。不過,為了讓代碼真正的具有可移植性,你還必須要將m_instance包裝成原子類型,并且用放松的原子操作(譯注:即非原子操作)來操作它。這里給出的是結果代碼,獲取與釋放屏障部分高亮了。

  1. std::atomic<Singleton*> Singleton::m_instance; 
  2. std::mutex Singleton::m_mutex; 
  3.  
  4. Singleton* Singleton::getInstance() { 
  5.     Singleton* tmp = m_instance.load(std::memory_order_relaxed); std::atomic_thread_fence(std::memory_order_acquire); if (tmp == nullptr) { 
  6.         std::lock_guard<std::mutex> lock(m_mutex); 
  7.         tmp = m_instance.load(std::memory_order_relaxed); if (tmp == nullptr) { 
  8.             tmp = new Singleton; std::atomic_thread_fence(std::memory_order_release); m_instance.store(tmp, std::memory_order_relaxed); 
  9.         } 
  10.     } 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獲取和釋放屏障的例子比起來,它基本上是等效的。

  1. mint_atomicPtr_t Singleton::m_instance = { 0 }; 
  2. mint_mutex_t Singleton::m_mutex; 
  3.  
  4. Singleton* Singleton::getInstance() { 
  5.     Singleton* tmp = (Singleton*) mint_load_ptr_relaxed(&m_instance); mint_thread_fence_acquire(); if (tmp == NULL) { 
  6.         mint_mutex_lock(&m_mutex); 
  7.         tmp = (Singleton*) mint_load_ptr_relaxed(&m_instance); if (tmp == NULL) { 
  8.             tmp = new Singleton; mint_thread_fence_release(); mint_store_ptr_relaxed(&m_instance, tmp); 
  9.         } 
  10.         mint_mutex_unlock(&m_mutex); 
  11.     } return tmp; 

#p#

使用c++ 11低級排序約束

C++11的獲取與釋放屏障可以正確的實現DCLP,而且應該能夠針對當今大多數的多核設備,生成優化的機器代碼(就像Mintomic做的那樣),但是它們似乎不是非常時髦。在C++11中獲得同等效果的首選方法,應該是使用基于低級排序約束的原子操作。正如我先前所說,一條寫釋放(write-release)可以同步于一條讀獲取(read-acquire)。

  1. std::atomic<Singleton*> Singleton::m_instance; 
  2. std::mutex Singleton::m_mutex; 
  3.  
  4. Singleton* Singleton::getInstance() { Singleton* tmp = m_instance.load(std::memory_order_acquire); if (tmp == nullptr) { 
  5.         std::lock_guard<std::mutex> lock(m_mutex); 
  6.         tmp = m_instance.load(std::memory_order_relaxed); if (tmp == nullptr) { 
  7.             tmp = new Singleton; m_instance.store(tmp, std::memory_order_release); } 
  8.     } 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實現。如之前所有例子一樣,一旦單例被創建,第二行高亮將與第一行同步

  1. std::atomic<Singleton*> Singleton::m_instance; 
  2. std::mutex Singleton::m_mutex; 
  3.  
  4. Singleton* Singleton::getInstance() { Singleton* tmp = m_instance.load(); if (tmp == nullptr) { 
  5.         std::lock_guard<std::mutex> lock(m_mutex); 
  6.         tmp = m_instance.load(); if (tmp == nullptr) { 
  7.             tmp = new Singleton; m_instance.store(tmp); } 
  8.     } return tmp; 

SC原子被認為可以使程序員更容易思考。其代價是生成的機器代碼似乎比之前的例子效率要低。例如,這里有有一些關于上面代碼清單的x64機器代碼,由Clang 3.3在啟用代碼優化的條件下生成:

由于我們使用了SC原子,保存到m_instance是由xchg指令實現的,在x64上它具有內存屏障作用。這比x64中DCLP實際需要的指令更強。 只需一條簡單的mov指令就可以做這項工作。不過這并不十分要緊,因為在單例首次創建的代碼路徑上,xchg指令只下發一次。

另一方面,如果你給PowerPC 或 ARMv6/v7編譯SC原子指令,你十有八九會得到糟糕的機器代碼。其中的細節,請看Herb Sutter的atomic<> 武器說話,第 2部分的00:44:25 - 00:49:16段落。

使用 C++11 的數據相關性排序

在上面所有我給出的例子中,在創建單例的那個線程,與其后任何越過鎖的線程之間,有一種同步的關系。警衛變量就是單例指針,有效載荷是單例自身的內容。在本例中,有效載荷被認為是警衛指針的一個相關性數據

人們后來發現,當存在數據相關性時,上面所有例子中都用到的讀獲取(read-acquire)操作,將極富殺傷力!我們用消費操作(consume operation)來替代它要好一點。消費操作很酷,因為它們消除了PowerPC中的一條lwsync指令,以及ARMv7中的一條dmb指令。在將來的一篇文章中,我將更多的談論到有關數據相關性和消費操作的內容。

使用C++11中的靜態初始化器

有些讀者已經知道這篇文章的妙語:如果你想得到一個線程安全的實例,C++11不允許你跳過以上的所有步驟。你可以簡單使用一個靜態初始化器。

  1. Singleton& Singleton::getInstance() {  
  2.     static Singleton instance; return instance; 

讓我們回到6.7.6節查看C++11的標準:

  如果控制進入申明同時變量將被初始化的時候,那么并發執行將會等到初始化的完成。

由編譯器來臨時代替實現的細節,DCLP明顯是一個不錯的選擇。不能保證編譯器將會使用DCLP,但一些(也許更多)卻碰巧發生了。使用the-std=c++0x選項對ARM進行編譯,生成了下面的一些機器碼,這些機器碼是由GCC 4.6生成的。

由于單例創建于固定地址,為了同步的目的,編譯器引進了一個獨立的警衛變量。特別需要注意的 是,在最初讀到這個警衛變量之后,并沒有現成的dmb指令可以用來獲取內存屏障。警衛變量是指向單例的指針,因此編譯器可以利用數據相關性,省略掉這種 dmb指令。__cxa_guard_release對警衛變量執行了一個寫釋放(write-release)操作,這樣只要警衛變量已設置,在讀消費 (read-consume)之前就建立了依賴順序,就像前面所有例子里那樣,基于對內存的重新排序,整個事情開始變得有彈性。

如你所見,我們已伴隨C++11走過了一段漫長的道路。雙重檢查鎖定是一種穩定的模式,而且還遠不止此!

就個人而言,我常常想,如果是需要初始化一個單例,最好是在程序啟動的時候做這個事情。但是顯然DCLP可以拯救你于泥潭。而且在實際的使用中,你還可以用DCLP來將任意數值類型存儲到一個無鎖的哈希表。在以后的文章中會有更多關于它的論述。

原文鏈接:http://preshing.com/20130930/double-checked-locking-is-fixed-in-cpp11/

譯文鏈接:http://www.oschina.net/translate/double-checked-locking-is-fixed-in-cpp11

責任編輯:陳四芳 來源: 開源中國編譯
相關推薦

2013-11-29 09:51:26

C++雙重檢查鎖定

2011-04-20 10:07:15

2016-11-11 00:33:25

雙重檢查鎖定延遲初始化線程

2024-02-21 23:43:11

C++11C++開發

2013-07-31 11:09:05

C++11

2023-09-22 22:27:54

autoC++11

2024-05-29 13:21:21

2020-06-01 21:07:33

C11C++11內存

2011-08-19 09:41:56

C++

2013-09-25 14:20:46

2013-05-30 00:49:36

C++11C++條件變量

2021-06-11 10:53:40

Folly組件開發

2020-12-09 10:55:25

ArrayvectorLinux

2023-09-24 13:58:20

C++1auto

2011-10-13 10:21:01

C++

2025-07-10 07:42:12

2020-09-23 16:31:38

C++C++11啟動線程

2025-12-15 06:05:00

C++11多線程shared_ptr

2012-12-25 10:52:23

IBMdW

2025-01-21 08:02:03

點贊
收藏

51CTO技術棧公眾號

成人动漫视频在线观看| 欧美另类自拍| 日韩成人在线观看视频| 91激情五月电影| 国产树林野战在线播放| 欧美日韩123| 日韩国产中文字幕| 亚洲黄色成人久久久| 国产精品xxx在线观看| 国产欧美日韩一区二区三区四区| 91精品国产综合久久香蕉的特点 | 欧美成熟毛茸茸复古| 国产精品传媒| 亚洲成人1234| 国产探花视频在线观看| 欧美性生交片4| 黄色国产网站| 欧美国产激情一区二区三区蜜月| 天堂а√在线中文在线 | 国产精品视频最多的网站| 日韩一二三区在线观看| 亚洲欧美精品在线| 国产激情在线观看| 欧美疯狂做受xxxx富婆| 香蕉网站在线| 欧美三级视频在线播放| 久操视频在线播放| 精品久久久久久久久久久院品网| 黄上黄在线观看| 日韩你懂的电影在线观看| 日韩黄色影院| 欧美日免费三级在线| 乱人伦中文视频在线| 偷窥少妇高潮呻吟av久久免费 | 成人精品在线视频观看| 激情网站五月天| 国产亚洲一二三区| 国产美女在线免费观看| 洋洋成人永久网站入口| av在线播放网站| 亚洲国产精品一区二区三区| 欧美xx视频| 久久不射电影网| 99国产精品一区二区| 亚洲精品一区二区毛豆| 久久视频一区二区| 亚洲成人av在线影院| 亚洲女成人图区| 国产成人一二| 欧美极品日韩| 日本一区二区免费在线观看视频 | av动漫在线看| 欧美特级www| 国产精品国产三级在线观看| 亚洲自拍小视频免费观看| 奇米四色…亚洲| 色综合久久天天综线观看| 强制捆绑调教一区二区| 欧美日韩视频免费| 一区二区三区高清| 高潮毛片在线观看| 亚洲精品中文字幕有码专区| 韩国精品福利一区二区三区| 国产久一道中文一区| 久久国产精品亚洲77777| 亚洲一二三四在线| 成人国产综合| 国产日韩欧美中文| 中文字幕av一区二区三区高| 国产精品久久久久9999| 黑人欧美xxxx| av网站网址在线观看| 久久久亚洲成人| 丝袜av一区| 亚洲一级片网站| 久久国产精品99国产精| 国产精品美女一区二区三区 | 91网站在线观看视频| 国产精品一区免费在线| 国产精品视频最多的网站| 久久99深爱久久99精品| 老司机在线视频二区| www.一区二区三区| 亚洲午夜精品久久久久久久久| 日韩第一区第二区| 中文字幕第80页| 欧美黑人国产人伦爽爽爽| 中文字幕乱码日本亚洲一区二区 | 性欧美hd调教| 日韩精品一区二区三区久久| 国产性色av一区二区| 国产麻豆精品一区二区| 激情久久99| 99色在线播放| 成人亚洲欧美一区二区三区| 亚洲激情六月丁香| 国产91xxx| 亚洲午夜久久久影院| 91女厕偷拍女厕偷拍高清| 精品三级av在线导航| 国产日韩中文字幕在线| 欧美一区二区在线免费观看| 麻豆精品在线观看| 亚洲黄页在线观看| 欧美jizzhd欧美| 亚洲专区在线| 美女av在线播放| 精品国产一区二区三区四区精华 | 强制捆绑调教一区二区| 国产午夜精品视频| 白白色 亚洲乱淫| 亚洲精品不卡在线观看 | 久久午夜色播影院免费高清| 深夜宅男网站免费进入| 亚洲一区精品视频| 性视频1819p久久| 精品亚洲夜色av98在线观看 | 鲁片一区二区三区| 欧美成人一二三| 欧美一区二区三区四区久久| 久久久国产精品麻豆 | 国产厕所精品在线观看| 91精品国产91久久久久久不卡| 欧美午夜理伦三级在线观看| 99re这里只有精品首页| 国产韩国精品一区二区三区| 红杏成人性视频免费看| av网站免费在线观看| 动漫成人在线| 黑森林精品导航| 白白操在线视频| 麻豆精品传媒视频| 国产欧美亚洲日本| 欧美中文字幕第一页| 日韩精品视频中文在线观看| 亚洲成人av电影| 亚洲国产va精品久久久不卡综合| 久久久久久久久岛国免费| 国产精品综合在线视频| 久久不射网站| 最新亚洲视频| 美国成人xxx| 美女一区二区在线观看| 亚洲国产欧美在线观看| 成人av地址| 天天躁日日躁成人字幕aⅴ| 精品一区亚洲| 亚洲精品成人| 欧美黄色一级视频| 最新国产精品久久久| 欧美不卡在线| 麻豆app在线观看| 在线观看免费视频一区二区三区| 日本桃色视频| 你懂的视频在线| 午夜av在线免费观看| 日韩av综合| 欧美午夜影院| 翔田千里一区二区| 91麻豆文化传媒在线观看| 亚洲中国最大av网站| 欧美挠脚心视频网站| 欧美精品免费在线| 91欧美精品午夜性色福利在线 | 992tv成人免费观看| 制服丝袜影音| 亚洲男同gay网站| 国产福利亚洲| 亚洲毛片免费看| 久久久人人人| 久久精品视频一区二区三区| 欧美中文字幕不卡| 欧美激情精品久久久久久免费印度| 国产精品欧美日韩一区二区| 国产伦精品一区二区三区视频孕妇| 777精品久无码人妻蜜桃| 在线播放色视频| 香蕉成人av| 秋霞一区二区三区| 国产一区激情| 成人免费小视频| 日韩精品中午字幕| 91视频免费网站| 久久久999视频| 亚洲精品天堂| 日韩av在线中文字幕| 综合电影一区二区三区| 精品网站999www| 精品91免费| 一不卡在线视频| 亚洲涩涩av| 中文字幕在线观看一区| 国产午夜精品理论片a级探花| 99re国产视频| 浪潮av在线| 人人玩人人添人人澡欧美| 欧美激情 亚洲a∨综合| 亚洲美女屁股眼交3| 欧美激情二区三区| 国产小视频精品|