多線程中局部靜態變量初始化的陷阱

C++當中常常需要一個全局唯一的對象實例,這時候,我們就會想到單件模式。如何實現這一模式?全局變量當然是一個簡單可行的方法,然而,這太丑陋。嗯,其實,丑陋倒也罷了,最嚴重的是它將引誘程序員濫用全局變量,這將導致維護的災難。
  
  既然全局變量是可能有害的,那么,我們我們把它隱藏一下,放到某個類當中去,作為類的靜態數據成員。這看上去不錯,我也這么認為。當我們只是簡單的需要一個全局對象時,這很好,而且足夠簡單。不過,天空中尚有一朵小小的烏云,讓我們來看一看它是什么。
  
  靜態成員變量的初始化,和全局對象一樣,實際上實在main函數進入后,我們寫下的一行代碼之前被執行的。而且,我們知道那個著名的初始化順序是不可靠的問題(跨編譯單元)。當我的全局對象是一個復雜對象――這很常見,比如一個環境管理器――它甚至還需要復雜的裝配過程,我們需要考慮:構建這個單件的時候,其對象都準備好了嗎?如果我們不能確定,那么一個常見的措施是延遲單件對象的構造――把它延遲到全局對象初始化結束以后怎么樣?這好像很容易實現:
  
  SomeClass * SomeClass ::instance(){
  static SomeClass inst;
  return &inst;
  }
  
  不錯吧?它不但可以延遲到全局對象初始化之后,甚至可以延遲到有人需要它的時候,才被構造出來,隨需應變,呵呵,是不是很帥?嗯,還有一點小問題,不僅存在對象初始化順序問題,析構也同樣存在問題。局部靜態變量的析構,和全局對象一樣,實在main函數退出前進行的,如果也要考慮順序問題的話...是不是有點麻煩呢?
  
  過度設計是一種罪,我是不是考慮的太復雜了?如果壓根就不需要考慮析構順序,這是不是很完美的解決方案?沒那么簡單!非但不夠完美,而且,這里面仍然存在缺陷:當我們運行在多線程環境的時候,靜態變量的初始化來實現單件,是不可靠的――直接的說,靜態變量有可能初始化多次!在作實驗之前,我們現分析一下靜態局部變量的實現方式,下面是前面instance實現的偽碼:
  
  if (!initialized){
  initialized = true;
  new (&inst)SomeClass;
  }
  return &inst;
  
  每個靜態變量都會擁有自己的初始化與否的標志,靜態變量初始化并不是一個原子操作,也沒有為多線程而設立互斥區(C++語言本身是沒有線程概念的),因此,我們要想實現多線程下的單件延遲創建,就不得不解決重復初始化的問題。至于如何實現,實際上這方面的代碼很多了。一個顯然的方案是設立互斥區,傳統的雙檢測技術可以有效解決這一問題――至少目前的C++是這樣,至于最近在csdn看到在Java中雙檢測失效的文章,我認為應該由Java語言負責。
  
  其實,局部靜態變量可能多次初始化,并不難理解,實踐上,也很少出嚴重的問題――出問題的條件還是挺苛刻的:多線程,不可多次初始化,恰好多個線程同時調用,恰好在if之后發生線程調度。很少出問題,不等于不出問題,特別的,對于廣泛使用的應用程序來說,出錯概率就不是一點點了。寫這篇東西的原因,是今天在公司看到的一段代碼,作了標識符替換:
  
  SomeClass * SomeClass::GetInstance(){
  static CLock g_lock;
  if (m_pInstance == NULL){
  g_lock.Lock();
  if (m_pInstance == NULL){
  m_pInstance = new SomeClass;
  }
  g_lock.Unlock();
  }
  return m_pInstance;
  }
  
  就這段代碼,雖然知道用雙檢測,且不說Lock/Unlock可以更好的處理,static CLock g_lock根本就是錯誤。鎖本身可能被初始化多次,象這種資源類型的對象,多次構造幾乎肯定會出錯的。而對于單件模式的實現,我認為,一般而言,Loki:: SingletonHolder會是一個好的選擇

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/444947.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/444947.shtml
英文地址,請注明出處:http://en.pswp.cn/news/444947.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MachineLearning(8)-PCA,LDA基礎+sklearn 簡單實踐

PCA,LDA基礎sklearn 簡單實踐1.PCAsklearn.decomposition.PCA1.PCA理論基礎2.sklearn.decomposition.PCA簡單實踐2.LDAsklearn.discriminant_analysis.LinearDiscriminantAnalysis2.1 LDA理論基礎2.2 sklearn LDA簡單實踐1.PCAsklearn.decomposition.PCA 1.PCA理論基礎 PCA:&…

引用變量和引用數組

前兩天沒事干,重拾C++的一些書籍,翻到引用這,無意寫了些DD: 其實引用和指針有很多相似的地方,又有不同的(太多了,不過說到效率上,比如函數傳參數,我們可以用引用,指針,哪種好呢,引用不必為站再分配空間了,而指針還學要分配4字節的空間給指針變量) 我們知道如何…

leetcode198 打家劫舍

你是一個專業的小偷,計劃偷竊沿街的房屋。每間房內都藏有一定的現金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警。 給定一個代表每個房屋存放金額的…

linux下的RPC

一、概述 在傳統的編程概念中,過程是由程序員在本地編譯完成,并只能局限在本地運行的一段代碼,也即其主程序和過程之間的運行關系是本地調用關系。因此這種結構在網絡日益發展的今天已無法適應實際需求。總而言之,傳統過程調用模式…

算法(28)--矩陣搜索系列

矩陣搜索1.leetcode-200. 島嶼數量2.leetcode-695. 島嶼的最大面積3.leetcode-463. 島嶼的周長4.劍指 Offer 12. 矩陣中的路徑5.leetcode-329. 矩陣中的最長遞增路徑6.leetcode-1091. 二進制矩陣中的最短路徑1.leetcode-200. 島嶼數量 給你一個由 ‘1’(陸地&#…

leetcode213 打家劫舍II

你是一個專業的小偷,計劃偷竊沿街的房屋,每間房內都藏有一定的現金。這個地方所有的房屋都圍成一圈,這意味著第一個房屋和最后一個房屋是緊挨著的。同時,相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚…

linux下安裝boost

以下是在ubuntu 7.10 (內核 2.6.22-14)下安裝的例子: 一、下載最新的 boost 庫,下載地址: http://www.boost.org/users/download/ 二、在適當的位置解壓 boost 庫,推薦把 boost 庫解壓到 /usr/local/ 下: $ cd dowlo…

PaperNotes(4)-高質量圖像生成-CGAN-StackGAN-Lapgan-Cyclegan-Pix2pixgan

cgan,stackgan,lapgan,cyclegan,pix2pixgan1.Conditional GAN1.1簡介1.2網絡結構與訓練1.3特點與用途2.Stack GAN2.1簡介2.2網絡結構與訓練2.3特點與用途3.Lap GAN3.1簡介3.2網絡結構與訓練3.3特點與用途4.Pix2pix GAN4.1 簡介4.2 網絡結構和訓練4.3 特點和用途5.Patch GAN6.Cy…

關于c++的一些案例

之前做項目的時候,有時候會用到位,也就是將一些數據放在二進制里,然后存在數據庫中或者緩存在服務器上,取出來,然后要判斷某位是不是置0或1,然后再將某位置0或1(比如領多個獎勵的 游戲邏輯),之前有點傻,竟然用 << ,>>這些運算符計算,今天翻起以前好久不…

C++(1)--概況、開發工具、hello word

簡介1. 概況2. 開發工具3. mac 寫hello word4. c 基本概念5.兩個數相加代碼分解5.1編譯預處理命令# include5.2輸入輸出庫iostream6.注釋7.編碼規范《老九學堂C課程》《C primer》學習筆記。《老九學堂C課程》詳情請到B站搜索《老九零基礎學編程C入門》1. 概況 20世紀70年代&a…

class 和 struct的區別

C中的struct對C中的struct進行了擴充&#xff0c;它已經不再只是一個包含不同數據類型的數據結構了&#xff0c;它已經獲取了太多的功能。 struct能包含成員函數嗎&#xff1f; 能&#xff01; struct能繼承嗎&#xff1f; 能&#xff01;&#xff01; struct能實現多態嗎&…

leetcode206 反轉鏈表

反轉一個單鏈表。 示例: 輸入: 1->2->3->4->5->NULL 輸出: 5->4->3->2->1->NULL 進階: 你可以迭代或遞歸地反轉鏈表。你能否用兩種方法解決這道題&#xff1f; 經典題不解釋 /*** Definition for singly-linked list.* public class ListNode…

淺議柔性數組

很多時候,柔性數組應用在了變長結構體中,如: StructPacket {Int state; Int len;

leetcode 152 乘積最大子序列

給定一個整數數組 nums &#xff0c;找出一個序列中乘積最大的連續子序列&#xff08;該序列至少包含一個數&#xff09;。 示例 1: 輸入: [2,3,-2,4] 輸出: 6 解釋: 子數組 [2,3] 有最大乘積 6。 示例 2: 輸入: [-2,0,-1] 輸出: 0 解釋: 結果不能為 2, 因為 [-2,-1] 不是子…

PaperNotes(5)-Conditional Generative Adversarial Nets

Conditional GAN 論文閱讀筆記Abstract1 Introduction2 Related Work3 Conditional Adversarial Nets3.1 Generative Adversarial Nets3.2 Conditional Adversarial Nets4 Experimental Results4.1 Unimodal4.2 Multimodal5 Future Work6.思考文章地址&#xff1a;https://arxi…

蛙泳姿勢教學

偶爾看到分享的一篇日志&#xff0c;記錄下&#xff0c;忙過這段時間努力學蛙泳。 蛙泳配合有一個順口溜&#xff0c;在講解蛙泳動作要領之前先介紹給大家&#xff1a;“劃手腿不動&#xff0c;收手再收腿&#xff0c;先伸胳膊后蹬腿&#xff0c;并攏伸直漂一會兒。”從順口溜中…

leetcode238 除本身以外數組的乘積

給定長度為 n 的整數數組 nums&#xff0c;其中 n > 1&#xff0c;返回輸出數組 output &#xff0c;其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘積。 示例: 輸入: [1,2,3,4] 輸出: [24,12,8,6] 說明: 請不要使用除法&#xff0c;且在 O(n) 時間復雜度內完…

C 和c++的一些雜想,想到哪兒寫到哪兒

關于C和c++一直有好多的程序猿在研究,研究區別研究相似的地方,究竟用那個預言好,沒有確定的說法,要看你做什么了。 初始化操作: 在初始化的時候,我們都知道C語言一般都是這樣處理的: int a=12; C++ 呢,除了這樣復制初始化之外還可以直接初始化: int a(12); 啊…

C++(2)--mac使用VScode 進行C++編譯、運行、調試

mac 使用VScode 進行C開發1.編譯的基礎概念2. mac 編譯c代碼2.1 查看編譯器情況2.2 安裝插件C/C&#xff0c;C/C Clang Command Adapte2.3新建一個C project2.3.1本地新建文件夾2.3.2新建mian.cpp文件2.3.3 編寫hello word demo2.4 代碼編譯&#xff0c;運行&#xff0c;調試2.…

boost庫linux編譯安裝

0.下載 1.解壓boost_1_49_0.tar.g然后放到/opt/ 2. 進入解壓后的文件夾 cd /opt/boost_1_49_0 3.將boost安裝配置在/boost/prefix目錄下 不過之前先 mkdir -p /boost/prefix