C++11的新特性
C++ 中有四種智能指針:auto_pt设江、unique_ptr、shared_ptr、weak_ptr 其中后三個(gè)是 C++11 支持末购,第一個(gè)已經(jīng)被 C++11 棄用且被 unique_prt 代替预伺,不推薦使用订咸。
各自的特點(diǎn)以及區(qū)別:
? ? 1、std::unique_ptr 對(duì)其持有的堆內(nèi)存具有唯一擁有權(quán)酬诀,也就是 std::unique_ptr 不可以拷貝或賦值給其他對(duì)象脏嚷,其擁有的堆內(nèi)存僅自己獨(dú)占,std::unique_ptr 對(duì)象銷毀時(shí)會(huì)釋放其持有的堆內(nèi)存料滥。鑒于 std::auto_ptr 的前車之鑒然眼,std::unique_ptr 禁止復(fù)制語(yǔ)義,為了達(dá)到這個(gè)效果葵腹,std::unique_ptr 類的拷貝構(gòu)造函數(shù)和賦值運(yùn)算符(operator =)被標(biāo)記為 delete高每。
? ? 2、std::shared_ptr 持有的資源可以在多個(gè) std::shared_ptr 之間共享践宴,每多一個(gè) std::shared_ptr 對(duì)資源的引用鲸匿,資源引用計(jì)數(shù)將增加 1,每一個(gè)指向該資源的 std::shared_ptr 對(duì)象析構(gòu)時(shí)阻肩,資源引用計(jì)數(shù)減 1带欢,最后一個(gè) std::shared_ptr 對(duì)象析構(gòu)時(shí)运授,發(fā)現(xiàn)資源計(jì)數(shù)為 0,將釋放其持有的資源乔煞。多個(gè)線程之間吁朦,遞增和減少資源的引用計(jì)數(shù)是安全的。(注意:這不意味著多個(gè)線程同時(shí)操作 std::shared_ptr 引用的對(duì)象是安全的)渡贾。std::shared_ptr 提供了一個(gè) use_count() 方法來(lái)獲取當(dāng)前持有資源的引用計(jì)數(shù)逗宜。除了上面描述的,std::shared_ptr 用法和 std::unique_ptr 基本相同空骚。
? ? 3纺讲、std::weak_ptr 是一個(gè)不控制資源生命周期的智能指針,是對(duì)對(duì)象的一種弱引用囤屹,只是提供了對(duì)其管理的資源的一個(gè)訪問(wèn)手段熬甚,引入它的目的為協(xié)助 std::shared_ptr 工作。
? ? ? ? ? std::weak_ptr 可以從一個(gè) std::shared_ptr 或另一個(gè) std::weak_ptr 對(duì)象構(gòu)造肋坚,std::shared_ptr 可以直接賦值給 std::weak_ptr 乡括,也可以通過(guò) std::weak_ptr 的 lock() 函數(shù)來(lái)獲得 std::shared_ptr。它的構(gòu)造和析構(gòu)不會(huì)引起引用計(jì)數(shù)的增加或減少冲簿。std::weak_ptr 可用來(lái)解決 std::shared_ptr 相互引用時(shí)的死鎖問(wèn)題(即兩個(gè)std::shared_ptr 相互引用粟判,那么這兩個(gè)指針的引用計(jì)數(shù)永遠(yuǎn)不可能下降為 0, 資源永遠(yuǎn)不會(huì)釋放)峦剔。
智能指針的大械到浮:一個(gè) std::unique_ptr 對(duì)象大小與裸指針大小相同(即?sizeof(std::unique_ptr) == sizeof(void)),而 std::shared_ptr 的大小是 std::unique_ptr 的一倍吝沫。
在 32 位機(jī)器上呻澜,std_unique_ptr 占 4 字節(jié),std::shared_ptr 和 std::weak_ptr 占 8 字節(jié)惨险。
在 64 位機(jī)器上羹幸,std_unique_ptr 占 8 字節(jié),std::shared_ptr 和 std::weak_ptr 占 16 字節(jié)辫愉。
也就是說(shuō)栅受,std_unique_ptr 的大小總是和原始指針大小一樣,std::shared_ptr 和 std::weak_ptr 大小是原始指針的一倍恭朗。
std::shared_ptr指針的實(shí)現(xiàn)原理:
實(shí)現(xiàn)原理:shared_ptr是利用一個(gè)計(jì)數(shù)器屏镊,無(wú)論我們使用拷貝構(gòu)造函數(shù)、賦值運(yùn)算符重載痰腮、作為函數(shù)返回值而芥、或作為參數(shù)傳給一個(gè)參數(shù)時(shí)計(jì)數(shù)器+1,當(dāng)shared_ptr被賦予一個(gè)新值或者需要銷毀時(shí)膀值,計(jì)數(shù)器-1棍丐,直到計(jì)數(shù)器為0時(shí)误辑,調(diào)用析構(gòu)函數(shù),釋放對(duì)象歌逢,并銷毀其內(nèi)存巾钉。shaerd_ptr不直接支持管理動(dòng)態(tài)數(shù)組,如果希望使用shared_ptr管理一個(gè)動(dòng)態(tài)數(shù)組趋翻,必須定制自己的刪除器睛琳。
多線程
線程與進(jìn)程之間的關(guān)系
????????進(jìn)程是資源擁有的單位盒蟆。線程是調(diào)度的最小單位踏烙。又稱為輕進(jìn)程。他只擁有進(jìn)程中一些資源历等,這次資源對(duì)于這個(gè)線程來(lái)說(shuō)是必不可少的讨惩。而所有的線程共享進(jìn)程的資源。因此不同的線程之間可以共享一些數(shù)據(jù)變量寒屯。而進(jìn)程則不可以荐捻,因?yàn)椴煌M(jìn)程有不同過(guò)的進(jìn)程控制塊,分別處于不同的物理地址空間里面寡夹。
????????每個(gè)線程都有自己的工作內(nèi)存处面,也就是棧,如果從更底層的來(lái)說(shuō)菩掏,相當(dāng)于硬件的高速緩存魂角,進(jìn)程的一些公共資源常存放在堆中也就是主內(nèi)存。不同線程之間的內(nèi)容切換智绸,首先要把各自的數(shù)據(jù)放到主內(nèi)存中野揪,才可以互相訪問(wèn)切換。
????????傳統(tǒng)的操作系統(tǒng)中瞧栗,沒(méi)有引入線程斯稳,則并發(fā)是指進(jìn)程。而對(duì)于進(jìn)程的并發(fā)調(diào)度迹恐,也就是上下文切換挣惰,需要浪費(fèi)極大地系統(tǒng)資源,例如CPU現(xiàn)場(chǎng)的保護(hù)殴边,寄存器憎茂,內(nèi)存地址的信息記錄,調(diào)度算法的執(zhí)行,以及進(jìn)程狀態(tài)的轉(zhuǎn)換如從運(yùn)行狀態(tài)轉(zhuǎn)換到阻塞狀態(tài)明刷,而且還要把把進(jìn)程轉(zhuǎn)到相應(yīng)的隊(duì)列里面驯妄。又因?yàn)橛行┻M(jìn)程只有一部分代碼需要?jiǎng)e的進(jìn)程提供一些數(shù)據(jù)。如果因?yàn)檫@一點(diǎn)的東西需要數(shù)據(jù)而阻塞赏枚,然后要發(fā)生強(qiáng)大的進(jìn)程切換亡驰,明顯是浪費(fèi)很多資源。
????????為此饿幅,在后來(lái)的操作系統(tǒng)中引入了線程概念凡辱。線程就是把一個(gè)進(jìn)程分成好多部分,每一個(gè)部分都是一個(gè)線程栗恩,這所有的線程透乾,共享進(jìn)程的資源。當(dāng)有一個(gè)線程需要?jiǎng)e的進(jìn)程提供數(shù)據(jù)磕秤,發(fā)生阻塞的時(shí)候乳乌,只有這個(gè)線程發(fā)生阻塞,其他的線程或許不需要這些數(shù)據(jù)也可以運(yùn)行市咆,因此只需要阻塞這個(gè)線程汉操。進(jìn)行單個(gè)線程切換,而整個(gè)進(jìn)程則不需要切換蒙兰,因此效率更高磷瘤。單個(gè)線程的調(diào)度只需要保存一些必要的信息,如寄存器值搜变,棧值子指針內(nèi)容采缚。線程切換,只需要更改一些寄存器的指針挠他,如PC,pd等寄存器扳抽,當(dāng)然這些是通過(guò)硬件實(shí)現(xiàn)的,效率是很快的绩社,然后指向當(dāng)前線程的代碼開(kāi)始地址處摔蓝,當(dāng)然對(duì)于源代碼來(lái)說(shuō),本質(zhì)上會(huì)經(jīng)過(guò)編譯程序和鏈接程序最終編程目標(biāo)代碼存放在內(nèi)存之中的某一空白的內(nèi)存地址處愉耙,并同時(shí)記錄該內(nèi)存地址的首地址贮尉,這些都是硬件級(jí)別的調(diào)用,只要要硬件的一些寄存器之類的東西轉(zhuǎn)換一下就可以朴沿。速度相當(dāng)快猜谚。并不需要什么進(jìn)程調(diào)度算法,這些很慢的調(diào)度赌渣。因此引入線程魏铅,后系統(tǒng)的并發(fā)度會(huì)更高。
線程的5種狀態(tài)及其之間的切換
如上圖所示:
我們可以清楚的看到線程的幾種狀態(tài)
????1.新建:使用NEW關(guān)鍵字來(lái)創(chuàng)建線程坚芜。
????2.可運(yùn)行:當(dāng)前線程調(diào)用start()方法览芳,使線程處于Runnable 狀態(tài),等待獲取CPU鸿竖。
????3.運(yùn)行中:如果線程搶到了CPU資源沧竟,這時(shí)的線程處于Running狀態(tài)铸敏,Runnable和Running是可以相互切換的,比如悟泵,其他優(yōu)先級(jí)較高線程搶占CPU資源杈笔,這時(shí)候線程就會(huì)變?yōu)镽unnable狀態(tài)。
? ? ? ?進(jìn)入Runnable狀態(tài)大體分為5種:
? ? ? ? ? ? ? ?線程調(diào)用sleep()方法經(jīng)過(guò)的時(shí)間超過(guò)了指定的時(shí)間糕非。
? ? ? ? ? ? ? ?線程正在等待某個(gè)通知蒙具,其他線程發(fā)出了通知。
? ? ? ? ? ? ? ?處于掛起的線程調(diào)用resume()方法朽肥。
? ? ? ? ? ? ? ?線程調(diào)用的阻塞IO已返回禁筏,阻塞方法執(zhí)行完畢。
? ? ? ? ? ? ? ?線程成功的獲取到了同步監(jiān)視器鞠呈。
? ? ? ?4.阻塞:出現(xiàn)Blocked的情況大概分為5種
? ? ? ? ? ? ? ?線程調(diào)用sleep()方法融师,主動(dòng)放棄占用的CPU資源。
? ? ? ? ? ? ? ?線程調(diào)用wait()方法蚁吝,等待某個(gè)通知。
? ? ? ? ? ? ? ?線程調(diào)用suspend()方法將線程掛起舀射,容易導(dǎo)致死鎖窘茁,盡量避免使用此方法。
? ? ? ? ? ? ? ?線程調(diào)用阻塞式IO方法脆烟,在方法返回前山林,線程被阻塞。
? ? ? ? ? ? ? ?線程試圖獲得一個(gè)同步監(jiān)視器邢羔,但該同步監(jiān)視器被其他線程所持有驼抹。
????5.死亡:run()方法運(yùn)行結(jié)束后進(jìn)入銷毀階段,整個(gè)線程執(zhí)行完畢拜鹤。
用戶級(jí)線程和內(nèi)核級(jí)線程
? ??????內(nèi)核級(jí)線程框冀,顧名思義,它的調(diào)度是依賴于操作系統(tǒng)的敏簿,即操作系統(tǒng)控制著內(nèi)核級(jí)線程的切換明也,比如有A和B兩個(gè)內(nèi)核級(jí)線程,我們用戶是不知道先執(zhí)行哪個(gè)線程的代碼和不知道什么時(shí)候切換到另一個(gè)線程執(zhí)行代碼的惯裕,這件事只有操作系統(tǒng)知道温数,我們無(wú)法干預(yù)。
? ??????用戶級(jí)線程蜻势,顧名思義撑刺,它的調(diào)度是依賴于用戶的想法的,比如有C和D兩個(gè)用戶級(jí)線程握玛,我們用戶可以先讓A執(zhí)行一段代碼后够傍,然后手動(dòng)控制讓其跳到B去執(zhí)行一段代碼次员,我們是清楚知道線程間的切換的。
????????簡(jiǎn)單一句話來(lái)說(shuō)就是:內(nèi)核級(jí)線程是由操作系統(tǒng)進(jìn)行調(diào)度的王带,用戶級(jí)線程是由用戶來(lái)控制調(diào)度的淑蔚。