AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當(dāng)前位置:首頁(yè) > 科技  > 軟件

解開(kāi)C++之call_once的神秘面紗:記一個(gè)有意思的問(wèn)題筆記

來(lái)源: 責(zé)編: 時(shí)間:2023-11-22 09:14:50 429觀看
導(dǎo)讀引言最近因?yàn)轫?xiàng)目要求用c++,之前一直很討厭c++,沒(méi)辦法只能短時(shí)間彌補(bǔ)c++的知識(shí),項(xiàng)目中需要一個(gè)接口只調(diào)用一次,需要使用到c++的call_once機(jī)制,于是寫(xiě)一個(gè)小demo來(lái)測(cè)試,就因?yàn)檫@個(gè)足夠小發(fā)現(xiàn)了一個(gè)非常有意思的問(wèn)題。call_on

引言

最近因?yàn)轫?xiàng)目要求用c++,之前一直很討厭c++,沒(méi)辦法只能短時(shí)間彌補(bǔ)c++的知識(shí),項(xiàng)目中需要一個(gè)接口只調(diào)用一次,需要使用到c++的call_once機(jī)制,于是寫(xiě)一個(gè)小demo來(lái)測(cè)試,就因?yàn)檫@個(gè)足夠小發(fā)現(xiàn)了一個(gè)非常有意思的問(wèn)題。fCf28資訊網(wǎng)——每日最新資訊28at.com

fCf28資訊網(wǎng)——每日最新資訊28at.com

call_once,基本原理

std::call_once 的內(nèi)部實(shí)現(xiàn)基于兩個(gè)重要的組件:std::once_flag 和 std::invoke。std::once_flag 是一個(gè)標(biāo)志,用于表示某個(gè)函數(shù)是否已經(jīng)被調(diào)用過(guò)。而 std::invoke 則負(fù)責(zé)實(shí)際調(diào)用該函數(shù)。fCf28資訊網(wǎng)——每日最新資訊28at.com

call_once的基本工作原理是:使用 std::once_flag 來(lái)標(biāo)記函數(shù)是否被調(diào)用過(guò)。當(dāng)有多個(gè)線程試圖調(diào)用 std::call_once 時(shí),只有一個(gè)線程會(huì)執(zhí)行函數(shù),其他線程會(huì)被阻塞直至該函數(shù)執(zhí)行完畢。fCf28資訊網(wǎng)——每日最新資訊28at.com

std::call_once 的使用步驟三步曲:fCf28資訊網(wǎng)——每日最新資訊28at.com

  • 創(chuàng)建 std::once_flag 對(duì)象:在需要保證函數(shù)只調(diào)用一次的地方創(chuàng)建一個(gè) std::once_flag 對(duì)象。
  • 編寫(xiě)需要執(zhí)行一次的函數(shù):編寫(xiě)你想要確保只調(diào)用一次的函數(shù)。
  • 調(diào)用 std::call_once:在需要執(zhí)行該函數(shù)的地方調(diào)用 std::call_once 并傳入 std::once_flag 和函數(shù)名稱。

demo問(wèn)題引入

demo非常簡(jiǎn)單,實(shí)現(xiàn)一個(gè)Init函數(shù)進(jìn)行call_once調(diào)用,只調(diào)用一次的函數(shù)Initialize做一次打印處理,main中連續(xù)調(diào)用Init 4次,理論上來(lái)說(shuō)我們執(zhí)行結(jié)果只有一行打印,這也是我們的目的。fCf28資訊網(wǎng)——每日最新資訊28at.com

#include <iostream>#include <thread>#include <mutex>#include <atomic>std::once_flag flag;void Initialize(){        std::cout << "Run into Initialize.." << std::endl;}void Init(){        std::call_once(flag, Initialize);}int main(){    Init();    Init();    Init();    Init();    return 0;}

使用g++編譯,執(zhí)行結(jié)果發(fā)現(xiàn)出錯(cuò)了:fCf28資訊網(wǎng)——每日最新資訊28at.com

fCf28資訊網(wǎng)——每日最新資訊28at.com

拋出了個(gè)異常,從call_once上的理解來(lái)說(shuō)代碼實(shí)現(xiàn)應(yīng)該是沒(méi)問(wèn)題的。于是使用調(diào)試大法gdb,編譯+g后使用gdb調(diào)試發(fā)現(xiàn)了個(gè)有意思的:fCf28資訊網(wǎng)——每日最新資訊28at.com

fCf28資訊網(wǎng)——每日最新資訊28at.com

fCf28資訊網(wǎng)——每日最新資訊28at.com

使用gdb調(diào)試發(fā)現(xiàn)__gthread_active_ptr指針是0,然后繼續(xù)執(zhí)行發(fā)現(xiàn)___gthread_once返回的__e為0,于是繼續(xù)執(zhí)行if就拋了異常。__gthread_active_ptr這又是什么呢?fCf28資訊網(wǎng)——每日最新資訊28at.com

深入研究研究

怎么看呢?既然不知道是什么我一般的操作是先看預(yù)處理部分代碼,使用gcc -E參數(shù)來(lái)編譯出call_once.i文件。fCf28資訊網(wǎng)——每日最新資訊28at.com

g++ -E call_once.cpp -o call_once.i

打開(kāi)call_once.i文件我發(fā)現(xiàn)main函數(shù)部分沒(méi)有什么特別之處,我們搜索call_once可以看到它的實(shí)現(xiàn)。fCf28資訊網(wǎng)——每日最新資訊28at.com

fCf28資訊網(wǎng)——每日最新資訊28at.com

這段代碼中的 std::call_once 函數(shù)首先創(chuàng)建了一個(gè)可調(diào)用對(duì)象 __callable,這個(gè)對(duì)象會(huì)調(diào)用傳入的函數(shù) __f,并傳入 __args 參數(shù)。然后,它將 __callable 的地址存儲(chǔ)到 __once_callable 變量中。fCf28資訊網(wǎng)——每日最新資訊28at.com

接下來(lái),通過(guò)一個(gè) lambda 表達(dá)式將 __callable 的調(diào)用封裝在 __once_call 中,這個(gè) lambda 表達(dá)式會(huì)執(zhí)行 __callable。fCf28資訊網(wǎng)——每日最新資訊28at.com

最后,使用底層線程庫(kù)的 __gthread_once 函數(shù)來(lái)確保 __once_call 只會(huì)執(zhí)行一次,即保證傳入的函數(shù) __f 只會(huì)被調(diào)用一次。fCf28資訊網(wǎng)——每日最新資訊28at.com

如果 __gthread_once 的返回值不為零,表示執(zhí)行出現(xiàn)了錯(cuò)誤,會(huì)通過(guò) __throw_system_error 拋出系統(tǒng)錯(cuò)誤。fCf28資訊網(wǎng)——每日最新資訊28at.com

既然是if(__e)后拋的異常,我們繼續(xù)看__gthread_once的實(shí)現(xiàn),搜索__gthread_once關(guān)鍵字,找到其實(shí)現(xiàn):fCf28資訊網(wǎng)——每日最新資訊28at.com

 11452 static inline int 11453 __gthread_once (__gthread_once_t *__once, void (*__func) (void)) 11454 { 11455   if (__gthread_active_p ()) 11456     return __gthrw_pthread_once (__once, __func); 11457   else 11458     return -1; 11459 }

這個(gè)函數(shù)可以看到執(zhí)行了__gthread_active_p ,我們繼續(xù)找__gthread_active_p 的實(shí)現(xiàn)。fCf28資訊網(wǎng)——每日最新資訊28at.com

fCf28資訊網(wǎng)——每日最新資訊28at.com

__gthread_active_p 是一個(gè)內(nèi)聯(lián)函數(shù),返回一個(gè)整數(shù)值。fCf28資訊網(wǎng)——每日最新資訊28at.com

static void const __gthread_active_ptr 是一個(gè)靜態(tài)指針常量,初始化為 __gthrw___pthread_key_create 函數(shù)的地址。fCf28資訊網(wǎng)——每日最新資訊28at.com

extension 是一個(gè) GNU C 擴(kuò)展,用于告知編譯器避免對(duì)某些表達(dá)式進(jìn)行警告。在此處,它將地址轉(zhuǎn)換為 void類型,以避免類型不匹配的警告。fCf28資訊網(wǎng)——每日最新資訊28at.com

&__gthrw___pthread_key_create 可能是一個(gè)特定線程庫(kù)(如 POSIX 線程庫(kù))內(nèi)部的函數(shù),用于創(chuàng)建線程特定數(shù)據(jù)的關(guān)鍵字。fCf28資訊網(wǎng)——每日最新資訊28at.com

函數(shù)返回 __gthread_active_ptr != 0,即如果該指針?lè)强?,則表明線程已激活(從指針命名上猜的)。fCf28資訊網(wǎng)——每日最新資訊28at.com

所以該函數(shù)用于指示線程是否被激活。不明白?我們繼續(xù)看__gthrw___pthread_key_create的定義。fCf28資訊網(wǎng)——每日最新資訊28at.com

11405 static __typeof(pthread_key_create) __gthrw___pthread_key_create __attribute__ ((__weakref__("__pthread_key_create")));

通過(guò) attribute((weakref("__pthread_key_create"))),將 __gthrw___pthread_key_create 弱引用到 __pthread_key_create。fCf28資訊網(wǎng)——每日最新資訊28at.com

弱引用是一種機(jī)制,允許在鏈接過(guò)程中,如果存在 __pthread_key_create 的定義,則使用它。但如果找不到 __pthread_key_create,則允許 __gthrw___pthread_key_create 仍然存在,只不過(guò)它將保持為空或未定義狀態(tài)。fCf28資訊網(wǎng)——每日最新資訊28at.com

這里大致就明白了,總結(jié)一下,call_once內(nèi)部實(shí)現(xiàn)中要找一個(gè)__pthread_key_create定義,如果不存在則返回空,即我們調(diào)試的時(shí)的指針給了0,從而引起異常。__pthread_key_create函數(shù)很明顯是線程函數(shù)。好,那我們?cè)诖a中加上該函數(shù)調(diào)用試試看。在main開(kāi)始增加如下:fCf28資訊網(wǎng)——每日最新資訊28at.com

int main(){    pthread_key_t key;    pthread_key_create(&key, NULL);    Init();

編譯運(yùn)行:fCf28資訊網(wǎng)——每日最新資訊28at.com

fCf28資訊網(wǎng)——每日最新資訊28at.com

達(dá)到預(yù)期效果了!fCf28資訊網(wǎng)——每日最新資訊28at.com

所以整體分析一下,在 call_once 的內(nèi)部實(shí)現(xiàn)中,檢測(cè)是否支持 __pthread_key_create 可能是為了確保在使用 call_once 進(jìn)行線程同步時(shí),能夠利用線程特定數(shù)據(jù)鍵來(lái)管理狀態(tài)或資源,確保其正確性和性能。如果當(dāng)前環(huán)境不支持 __pthread_key_create,那么在多線程環(huán)境下可能無(wú)法有效地管理線程特定的狀態(tài)信息。fCf28資訊網(wǎng)——每日最新資訊28at.com

因此,檢測(cè)是否支持 __pthread_key_create 可能是 call_once 實(shí)現(xiàn)中的一種策略,用于在可能的情況下提供更好的線程安全性和性能。如果當(dāng)前環(huán)境不支持這個(gè)特定的功能,可能會(huì)采用其他方式來(lái)實(shí)現(xiàn) call_once,或者簡(jiǎn)化其行為以確保程序在這樣的環(huán)境中仍能正確運(yùn)行,盡管可能會(huì)犧牲一些特定的功能或性能。fCf28資訊網(wǎng)——每日最新資訊28at.com

我嘗試不使用__pthread_key_create,隨便調(diào)用一個(gè)pthread庫(kù)的api,比如pthread_create,或者pthread_mutex_init,調(diào)用可以全部傳遞空指針,結(jié)果依然是可以達(dá)到預(yù)期的。所以驗(yàn)證也說(shuō)明call_once內(nèi)部通過(guò)弱引用庫(kù)函數(shù)來(lái)檢測(cè)當(dāng)前是否支持多線程,如果不支持則拋出異常,所以使用前提必須是多線程環(huán)境。fCf28資訊網(wǎng)——每日最新資訊28at.com

總結(jié)

call_once 的魅力與注意事項(xiàng):fCf28資訊網(wǎng)——每日最新資訊28at.com

std::call_once 提供了一種簡(jiǎn)單而又強(qiáng)大的多線程同步方式,但在使用時(shí)也需注意一些細(xì)節(jié)。比如一定要確保程序是多線程調(diào)用,如果有多線程自然還要確保線程安全,避免潛在的死鎖和競(jìng)態(tài)條件問(wèn)題登。fCf28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.tebozhan.com/showinfo-26-33372-0.html解開(kāi)C++之call_once的神秘面紗:記一個(gè)有意思的問(wèn)題筆記

聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: C++高級(jí)編程:構(gòu)建高效穩(wěn)定接口與深入對(duì)象設(shè)計(jì)技巧

下一篇: 使用單例模式管理全局音頻

標(biāo)簽:
  • 熱門焦點(diǎn)
Top