色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

基于shared_ptr的C++非侵入式引用計數(shù)解決方案的缺陷

Linux愛好者 ? 來源:Linux愛好者 ? 作者:Linux愛好者 ? 2022-06-13 14:24 ? 次閱讀

問題描述

在基于C++的大型系統(tǒng)的設(shè)計實現(xiàn)中,由于缺乏語言級別的GC支持,資源生存周期往往是一個棘手的問題。系統(tǒng)地解決這個問題的方法無非兩種:

  • 使用GC庫
  • 使用引用計數(shù)

嚴(yán)格地說,引用計數(shù)其實也是一種最樸素的GC。相對于現(xiàn)代的GC技術(shù),引用計數(shù)的實現(xiàn)簡單,但相應(yīng)地,它也存在著循環(huán)引用和線程同步開銷等問題。

關(guān)于這二者孰優(yōu)孰劣,已經(jīng)有過很多討論,在此就不攪這股混水了。我一直也沒有使用過C++的GC庫,在實際項目中總是采用引用計數(shù)的方案。而作為Boost的擁躉,首選的自然是shared_ptr。一直以來我也對shared_ptr百般推崇,然而最近的一些項目開發(fā)經(jīng)驗卻讓我在shared_ptr上栽了坑,對C++引用計數(shù)也有了一些新的的認(rèn)識,遂記錄在此。

本文主要針對基于boost::shared_ptr的C++引用計數(shù)實現(xiàn)方案進(jìn)行一些討論。C++引用計數(shù)方案往往伴隨著用于自動管理引用計數(shù)的智能指針。按是否要求資源對象自己維護(hù)引用計數(shù),C++引用計數(shù)方案可以分為兩類:

  • 侵入式:侵入式的引用計數(shù)管理要求資源對象本身維護(hù)引用計數(shù),同時提供增減引用計數(shù)的管理接口。通常侵入式方案會提供配套的侵入式引用計數(shù)智能指針。該智能指針通過調(diào)用資源對象的引用計數(shù)管理接口來自動增減引用計數(shù)。COM對象與CComPtr便是侵入式引用計數(shù)的一個典型實例。
  • 非侵入式:非侵入式的引用計數(shù)管理對資源對象本身沒有任何要求,而是完全借助非侵入式引用計數(shù)智能指針在資源對象外部維護(hù)獨立的引用計數(shù)。shared_ptr便是基于這個思路。

第一宗罪

初看起來,非侵入式方案由于對資源對象的實現(xiàn)沒有任何要求,相較于侵入式方案更具吸引力。然而事實卻并非如此。下面就來分析一下基于shared_ptr的非侵入式引用計數(shù)。在使用shared_ptr的引用計數(shù)解決方案中,引用計數(shù)完全由shared_ptr控制,資源對象對與自己對應(yīng)的引用計數(shù)一無所知。

而引用計數(shù)與資源對象的生存期息息相關(guān),這就意味著資源對象喪失了對生存期的控制權(quán),將自己的生殺大權(quán)拱手讓給了shared_ptr。這種情況下,資源對象就不得不依靠至少一個shared_ptr實例來保障自己的生存。換言之,資源對象一旦“沾染”了shared_ptr,就一輩子都無法擺脫!考察以下的簡單用例:

用例一:

Resource*p=newCResource;
{
shared_ptrq(p);
}
p->Use()//CRASH

單純?yōu)榱私鉀Q上述的崩潰,可以自定義一個什么也不做的deleter:

structnoop_deleter{
voidoperator()(void*){
//NO-OP
}
};

然后將上述用例的第三行改為:

shared_ptrq(p,noop_deleter());

但是這樣一來,shared_ptr就喪失了借助RAII自動釋放資源的能力,違背了我們利用智能指針自動管理資源生存期的初衷(話說回來,這倒并不是說noop_deleter這種手法毫無用處,Boost.Asio中就巧妙地利用shared_ptr、weak_ptr和noop_deleter來實現(xiàn)異步I/O事件的取消)。

從這個簡單的用例可以看出,shared_ptr就像是毒品一樣,一旦沾染就難以戒除。更甚者,染毒者連換用其他“毒品”的權(quán)力都沒有:shared_ptr的引用計數(shù)管理接口是私有的,無法從shared_ptr之外操控,也就無法從shared_ptr遷移到其他類型的引用計數(shù)智能指針。

不僅如此,資源對象沾染上shared_ptr之后,就只能使用最初的那個shared_ptr實例的拷貝來維系自己的生存期。考察以下用例:用例二:

{
shared_ptrp1(newCResource);
shared_ptrp2(p1);//OK
CResource*p3=p1.get();
shared_ptrp4(p3);//ERROR
//CRASH
}

該用例的執(zhí)行過程如下:

  1. p1在構(gòu)造的同時為資源對象創(chuàng)建了一份外部引用計數(shù),并將之置為1
  2. p2拷貝自p1,與p1共享同一個引用計數(shù),將之增加為2
  3. p4并非p1的拷貝,因此在構(gòu)造的同時又為資源對象創(chuàng)建了另外一個外部引用計數(shù),并將之置為1
  4. 在作用域結(jié)束時,p4析構(gòu),由其維護(hù)的額外的引用計數(shù)降為0,導(dǎo)致資源對象被析構(gòu)
  5. 然后p2析構(gòu),對應(yīng)的引用計數(shù)降為1
  6. 接著p1析構(gòu),對應(yīng)的引用計數(shù)也歸零,于是p1在臨死之前再次釋放資源對象 最后,由于資源對象被二次釋放,程序崩潰

至此,我們已經(jīng)認(rèn)識到了shared_ptr的第一宗罪——傳播毒品

  • 毒性一:一旦開始對資源對象使用shared_ptr,就必須一直使用
  • 毒性二:無法換用其他類型的引用計數(shù)之智能指針來管理資源對象生存期
  • 毒性三:必須使用最初的shared_ptr實例拷貝來維系資源對象生存期

第二宗罪

乘勝追擊,再揭露一下shared_ptr的第二宗罪——散布病毒。有點聳人聽聞了?其實道理很簡單:由于使用了shared_ptr的資源對象必須仰仗shared_ptr的存在才能維系生存期,這就意味著使用資源的客戶對象也必須使用shared_ptr來持有資源對象的引用——于是shared_ptr的勢力范圍成功地從資源對象本身擴(kuò)散到了資源使用者,侵入了資源客戶對象的實現(xiàn)

同時,資源的使用者往往是通過某種形式的資源分配器來獲取資源。自然地,為了向客戶轉(zhuǎn)交資源對象的所有權(quán),資源分配器也不得不在接口中傳遞shared_ptr,于是shared_ptr也會侵入資源分配器的接口

有一種情況可以暫時擺脫shared_ptr,例如:

shared_ptrAllocateResource(){
shared_ptrpResource(newCResource);
InitResource(pResource.get());
returnpResource;
}
voidInitResource(IResource*r){
//Doresourceinitialization...
}

以上用例中,在InitResource的執(zhí)行期間,由于AllocateResource的堆棧仍然存在,pResource不會析構(gòu),因此可以放心的在InitResource的參數(shù)中使用裸指針傳遞資源對象。這種基于調(diào)用棧的引用計數(shù)優(yōu)化,也是一種常用的手段。但在InitResource返回后,資源對象終究還是會落入shared_ptr的魔掌。

由此可以看出,shared_ptr打著“非侵入式”的幌子,雖然沒有侵入資源對象的實現(xiàn),卻侵入了資源分配接口以及資源客戶對象的實現(xiàn)。而沾染上shared_ptr就擺脫不掉,如此傳播下去,簡直就是侵入了除資源對象實現(xiàn)以外的其他各個地方!這不是病毒是什么?

然而,基于shared_ptr的引用計數(shù)解決方案真的不會侵入資源對象的實現(xiàn)嗎?

第三宗罪

在一些用例中,資源對象的成員方法(不包括構(gòu)造函數(shù))需要獲取指向?qū)ο笞陨恚窗藅his指針的shared_ptr。Boost.Asio的chat示例便展示了這樣一個用例:chat_session對象會在其成員函數(shù)中發(fā)起異步I/O操作,并在異步I/O操作回調(diào)中保存一個指向自己的shared_ptr以保證回調(diào)執(zhí)行時自身的生存期尚未結(jié)束。

這種手法在Boost.Asio中非常常見,在不考慮shared_ptr帶來的麻煩時,這實際上也是一種相當(dāng)優(yōu)雅的異步流程資源生存期處理方法。但現(xiàn)在讓我們把注意力集中在shared_ptr上。

通常,使用shared_ptr的資源對象必須動態(tài)分配,最常見的就是直接從堆上new出一個實例并交付給一個shared_ptr,或者也可以從某個資源池中分配再借助自定義的deleter在引用計數(shù)歸零時將資源放回池中。無論是那種用法,該資源對象的實例在創(chuàng)建出來后,都總是立即交付給一個shared_ptr(記為p)。

有鑒于之前提到的毒性三,如果資源對象的成員方法需要獲取一個指向自己的shared_ptr,那么這個shared_ptr也必須是p的一個拷貝——或者更本質(zhì)的說,必須與p共享同一個外部引用計數(shù)。然而對于資源對象而言,p維護(hù)的引用計數(shù)是外部的陌生事物,資源對象如何得到這個引用計數(shù)并由此構(gòu)造出一個合法的shared_ptr呢?這是一個比較tricky的過程。為了解決這個問題,Boost提供了一個類模板enable_shared_from_this:

所有需要在成員方法中獲取指向this的shared_ptr的類型,都必須以CRTP手法繼承自enable_shared_from_this。即:

classCResource:
publicboost::enable_shared_from_this
{
//...
};

接著,資源對象的成員方法就可以使用enable_shared_from_this::shared_from_this()方法來獲取所需的指向?qū)ο笞陨淼膕hared_ptr了。問題似乎解決了。但是,等等!這樣的繼承體系不就對資源對象的實現(xiàn)有要求了嗎?換言之,這不正是對資源對象實現(xiàn)的赤裸裸的侵入嗎?這正是shared_ptr的第三宗罪——欺世盜名

第四宗罪

最后一宗罪,是鋪張浪費。對了,說的就是性能。

基于引用計數(shù)的資源生存期管理,打一出生起就被扣著線程同步開銷大的帽子。早期的Boost版本中,shared_ptr是借助Boost.Thread的mutex對象來保護(hù)引用計數(shù)。在后期的版本中采用了lock-free的原子整數(shù)操作一定程度上降低了線程同步開銷。然而即使是lock-free,本質(zhì)上也仍然是串行化訪問,線程同步的開銷多少都會存在。

也許有人會說這點開銷與引用計數(shù)帶來的便利相比算不得什么。然而在我們項目的異步服務(wù)器框架的壓力測試中,大量引用計數(shù)的增減操作,一舉吃掉了5%的CPU。換言之,1/20的計算能力被浪費在了與業(yè)務(wù)邏輯完全無關(guān)的引用計數(shù)的維護(hù)上!而且,由于是異步流程的特殊性,也無法應(yīng)用上面提及的基于調(diào)用棧的引用計數(shù)優(yōu)化。

那么針對這個問題就真的沒有辦法了嗎?其實仔細(xì)檢視一下整個異步流程,有些資源雖然會先后被不同的對象所引用,但在其整個生存周期內(nèi),每一時刻都只有一個對象持有該資源的引用。用于數(shù)據(jù)收發(fā)的緩沖區(qū)對象就是一個典型。它們總是被從某個源頭產(chǎn)生,然后便一直從一處被傳遞到另一處,最終在某個時刻被回收。

對于這樣的對象,實際上沒有必要針對流程中的每一次所有權(quán)轉(zhuǎn)移都進(jìn)行引用計數(shù)操作,只要簡單地在分配時將引用計數(shù)置1,在需要釋放時再將引用計數(shù)歸零便可以了。

對于侵入式引用計數(shù)方案,由于資源對象自身持有引用計數(shù)并提供了引用計數(shù)的操作接口,可以很容易地實現(xiàn)這樣的優(yōu)化。但shared_ptr則不然。shared_ptr把引用計數(shù)牢牢地攥在手中,不讓外界碰觸;外界只有通過shared_ptr的構(gòu)造函數(shù)、析夠函數(shù)以及reset()方法才能夠間接地對引用計數(shù)進(jìn)行操作。

而由于shared_ptr的毒品特性,資源對象無法脫離shared_ptr而存在,因此在轉(zhuǎn)移資源對象的所有權(quán)時,也必須通過拷貝shared_ptr的方式進(jìn)行。一次拷貝就對應(yīng)一對引用計數(shù)的原子增減操作。

對于上述的可優(yōu)化資源對象,如果在一個流程中被傳遞3次,除去分配和釋放時的2次,還會導(dǎo)致6次無謂的原子整數(shù)操作。整整浪費了300%!

事實證明,在將基于shared_ptr的非侵入式引用計數(shù)方案更改為侵入式引用計數(shù)方案并施行上述優(yōu)化后,我們的異步服務(wù)器框架的性能有了明顯的提升。

結(jié)語

最后總結(jié)一下shared_ptr的四宗罪:

  • 傳播毒品一旦對資源對象染上了shared_ptr,在其生存期內(nèi)便無法擺脫。
  • 散布病毒在應(yīng)用了shared_ptr的資源對象的所有權(quán)變換的整個過程中的所有接口都會受到shared_ptr的污染。
  • 欺世盜名在enable_shared_from_this用例下,基于shared_ptr的解決方案并非是非侵入式的。
  • 鋪張浪費由于shared_ptr隱藏了引用計數(shù)的操作接口,只能通過拷貝shared_ptr的方式間接操縱引用計數(shù),使得用戶難以規(guī)避不必要的引用計數(shù)操作,造成無謂的性能損失。

探明這四宗罪算是最近一段時間的項目設(shè)計開發(fā)過程的一大收獲。寫這篇文章的目的不是為了將shared_ptr一棒子打死,只是為了總結(jié)基于shared_ptr的C++非侵入式引用計數(shù)解決方案的缺陷,也讓自己不再盲目迷信shared_ptr。

原文標(biāo)題:共享指針?biāo)淖谧?/p>

文章出處:【微信公眾號:Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 指針
    +關(guān)注

    關(guān)注

    1

    文章

    481

    瀏覽量

    70595
  • C++
    C++
    +關(guān)注

    關(guān)注

    22

    文章

    2114

    瀏覽量

    73793
  • 計數(shù)
    +關(guān)注

    關(guān)注

    1

    文章

    57

    瀏覽量

    20127

原文標(biāo)題:共享指針?biāo)淖谧?/p>

文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    記一次詭異的內(nèi)存泄漏

    最近在補(bǔ)一些基礎(chǔ)知識,恰好涉及到了智能指針std::weak_ptr在解決std::shared_ptr時候循環(huán)引用的問題
    的頭像 發(fā)表于 02-19 13:44 ?695次閱讀
    記一次詭異的內(nèi)存泄漏

    C++標(biāo)準(zhǔn)庫第二版》總結(jié)濃縮精華

    。(2)scoped_array:與scoped_ptr的唯一不同是動態(tài)數(shù)組,單個對象。除非對性能有非常苛刻要求,或編譯器不支持標(biāo)準(zhǔn)庫,否則不建議使用。(3)shared_ptr:與unique_
    發(fā)表于 10-23 20:31

    《Boost程序完全開發(fā)指南》重點總結(jié)分享

    )scoped_array:與scoped_ptr的唯一不同是動態(tài)數(shù)組,單個對象。除非對性能有非常苛刻要求,或編譯器不支持標(biāo)準(zhǔn)庫,否則不建議使用。(3)shared_ptr:與unique_
    發(fā)表于 10-23 20:33

    C陷阱與缺陷CC++經(jīng)典著作)

    電子發(fā)燒友網(wǎng)站提供《C陷阱與缺陷CC++經(jīng)典著作).txt》資料免費下載
    發(fā)表于 06-03 16:26 ?0次下載

    基于模板濾波的侵入負(fù)荷辨識系統(tǒng)

    針對侵入負(fù)荷監(jiān)測系統(tǒng)在設(shè)備投入、復(fù)雜性以及擴(kuò)展性上的缺陷,在詳細(xì)分析侵入負(fù)荷監(jiān)測系統(tǒng)工作原
    發(fā)表于 02-01 13:51 ?0次下載
    基于模板濾波的<b class='flag-5'>非</b><b class='flag-5'>侵入</b>負(fù)荷辨識系統(tǒng)

    侵入自動測試系統(tǒng)的研制

    侵入系統(tǒng)的關(guān)鍵技術(shù)點。最后結(jié)合現(xiàn)場實際運行,提出引入了侵入概念后的安全實施
    發(fā)表于 03-29 11:06 ?0次下載

    ABB全新的侵入溫度解決方案榮獲德國創(chuàng)新獎

    這款侵入溫度傳感器的誕生標(biāo)志著溫度測量新紀(jì)元的開啟。
    的頭像 發(fā)表于 06-04 09:29 ?3944次閱讀

    C++引用的使用場景

    C++引用的使用場景
    的頭像 發(fā)表于 06-29 15:18 ?4107次閱讀
    <b class='flag-5'>C++</b>:<b class='flag-5'>引用</b>的使用場景

    C++中的const和引用的討論

    今天給大家分享一下這段時間學(xué)習(xí)c++的總結(jié)學(xué)習(xí):c++里面的const關(guān)鍵字和引用
    的頭像 發(fā)表于 12-24 15:35 ?877次閱讀

    C++基礎(chǔ)語法中的引用、封裝和多態(tài)

    本期是C++基礎(chǔ)語法分享的第六節(jié),今天給大家來分享一下: (1)引用; (2)宏; (3)成員初始化列表; (4)封裝; (5)繼承; (6)多態(tài); 引用 左值引用 常規(guī)
    的頭像 發(fā)表于 09-12 09:58 ?1338次閱讀

    C++中的智能指針

    C++11中,有unique_ptrshared_ptr以及weak_ptr三種,auto_ptr因為自身轉(zhuǎn)移所有權(quán)的原因,在
    的頭像 發(fā)表于 08-05 11:11 ?936次閱讀

    檢測并強(qiáng)化對侵入篡改的攻擊

    檢測并強(qiáng)化對侵入篡改的攻擊
    發(fā)表于 11-01 08:25 ?0次下載
    檢測并強(qiáng)化對<b class='flag-5'>非</b><b class='flag-5'>侵入</b><b class='flag-5'>式</b>篡改的攻擊

    C++引用和指針

    之前的文章我們已經(jīng)介紹了C++中的基本類型如int,bool和double等,除了基本類型C++還有一些更復(fù)雜的數(shù)據(jù)類型復(fù)合類型,所謂的復(fù)合類型就是通過其他類型定義的類型,本篇文章我們將會著重介紹C++的復(fù)合類型
    的頭像 發(fā)表于 03-17 14:00 ?664次閱讀

    HARDWARIO侵入監(jiān)測-旋轉(zhuǎn)監(jiān)測

    電子發(fā)燒友網(wǎng)站提供《HARDWARIO侵入監(jiān)測-旋轉(zhuǎn)監(jiān)測.zip》資料免費下載
    發(fā)表于 06-16 10:03 ?0次下載
    HARDWARIO<b class='flag-5'>非</b><b class='flag-5'>侵入</b><b class='flag-5'>式</b>監(jiān)測-旋轉(zhuǎn)監(jiān)測

    C++智能指針的底層實現(xiàn)原理

    C++智能指針的頭文件: #include 1. shared_ptr: 智能指針從本質(zhì)上來說是一個模板類,用類實現(xiàn)對指針對象的管理。 template class shared_ptr
    的頭像 發(fā)表于 11-09 14:32 ?783次閱讀
    <b class='flag-5'>C++</b>智能指針的底層實現(xiàn)原理
    主站蜘蛛池模板: 坠落的丝袜美人妻 | 40分钟超爽大片黄 | 少妇一夜未归暴露妓女身份 | 中文字幕久久熟女人妻AV免费 | 麻豆AV无码精品一区二区 | 伊人久久精品中文字幕 | 免费啪视频观试看视频 | 国产精品禁18久久久夂久 | 亚洲精品久久YY5099 | 国产精品悠悠久久人妻精品 | 擦擦擦在线视频观看 | 999久久久国产精品蜜臀AV | 99热只有精品 | 国内精品久久久久久久999下 | 香蕉久久夜色精品国产小优 | 午夜精品久久久内射近拍高清 | 爱情岛论坛网亚洲品质 | 久久re6热在线视频精品66 | 中文字幕国产在线观看 | 在线视频一区二区三区在线播放 | 国产亚洲福利精品一区 | 麻豆精品传媒一二三区 | 影888午夜理论不卡 樱桃熟了A级毛片 | 2022久久精品国产色蜜蜜麻豆 | 成人欧美尽粗二区三区AV | 琉璃美人煞在线观看 | 亚洲 自拍 欧洲 视频二区 | 亚洲 欧美 中文 日韩 另类 | 伊人综合在线22 | 免费在线视频一区 | 久久中文电影 | 草草久久久无码国产专区全集观看 | 巨乳中文无码亚洲 | 亚洲男人的天堂久久精品麻豆 | 亚洲精品福利一区二区在线观看 | 亚洲精品一区国产欧美 | 欧美18精品久久久无码午夜福利 | 欧美怡红院视频一区二区三区 | 桃隐社区最新最快地址 | 99国产精品人妻无码免费 | 亚洲不卡视频 |