Linux: 多线程
目錄
線程概念:
線程之間的獨(dú)有與共享:
多線程與多進(jìn)程在多任務(wù)處理中的優(yōu)缺點(diǎn):
線程控制:
線程創(chuàng)建:
線程終止:
線程等待:
線程分離:
線程安全(的問(wèn)題):
*互斥鎖:
*死鎖:?
*同步:
*條件變量:
設(shè)計(jì)模式 (多線程的應(yīng)用): 生產(chǎn)者與消費(fèi)者模型
*信號(hào)量(POSIX):posix標(biāo)準(zhǔn)信號(hào)量:
*線程池的簡(jiǎn)單實(shí)現(xiàn):
*線程安全的單例模式:
線程概念:
線程是進(jìn)程中的一條執(zhí)行流程.
在linux之前學(xué)習(xí)進(jìn)程的時(shí)候 ,進(jìn)程就是一個(gè)pcb, 但是在現(xiàn)在學(xué)習(xí)線程的時(shí)候,? 發(fā)現(xiàn)線程是進(jìn)程中的一條執(zhí)行流,而因?yàn)?strong>linux下執(zhí)行流是通過(guò)pcb來(lái)完成的,所以理解pcb是linux下的執(zhí)行流,反推得到了一個(gè)結(jié)論,linux下的一個(gè)pcb是一個(gè)線程。只不過(guò)人家linux下通常不談線程,而叫做輕量級(jí)進(jìn)程. ( 有些地方認(rèn)為L(zhǎng)inux沒(méi)有真正的線程的說(shuō)法, 線程實(shí)際上是一個(gè)輕量級(jí)進(jìn)程. )
從另一個(gè)角度來(lái)說(shuō):
線程是cpu調(diào)度的基本單位, 進(jìn)程是資源分配的基本單位.
線程之間的獨(dú)有與共享:
共享(每個(gè)線程相同的):?
虛擬地址空間(使線程間可以直接通信).
信號(hào)處理方式(信號(hào)是針對(duì)進(jìn)程的, 當(dāng)給一個(gè)進(jìn)程發(fā)送一個(gè)信號(hào)時(shí), 所有的線程都能收到這個(gè)信號(hào),? 處于cpu時(shí)間片上運(yùn)行的線程會(huì)處理這個(gè)信號(hào), 某個(gè)地方修改了這個(gè)信號(hào)的處理方式則其他線程對(duì)這個(gè)信號(hào)的處理方式也跟著修改了.).?
io信息(共享文件描述信息, 可以操作同一文件, 而且不同的線程的文件讀寫(xiě)位置是一致的, 常見(jiàn)的設(shè)計(jì)思路是 有一個(gè)線程專(zhuān)門(mén)負(fù)責(zé)打開(kāi)文件,? 后續(xù)其他線程負(fù)責(zé)文件的各種操作).
工作路徑( 比如在三級(jí)目錄下運(yùn)行一個(gè)open(./text, O_CREATE,664)的test程序則text文件生成在當(dāng)前三記錄下. 如果在二級(jí)目錄下運(yùn)行這個(gè)test程序:?./三級(jí)目錄名/test 運(yùn)行則text文件生成在當(dāng)前的二級(jí)目錄下)等...
獨(dú)有(線程之間不同的):
棧( 局部變量存放在棧中, 但是把局部變量的地址給其他線程, 其他線程也能訪問(wèn)到該變量,因?yàn)樘摂M地址空間是同一套的.).
上下文數(shù)據(jù)(即寄存器獨(dú)有pcb是不斷在切換運(yùn)行的, 為了保存不同線程自己每次運(yùn)行到哪了,下次時(shí)間片運(yùn)行時(shí)接著運(yùn)行,所以每個(gè)線程有自己的上下文數(shù)據(jù).).
errno(不同線程用接口操作時(shí)某個(gè)可能失敗了, 某個(gè)成功了, errno獨(dú)有則保證了不發(fā)生沖突).
信號(hào)屏蔽字(即信號(hào)阻塞集合, 線程信號(hào)會(huì)打斷當(dāng)前操作, 為了保護(hù)某些線程正常運(yùn)行, 就算此線程拿到時(shí)間片也不去處理信號(hào). 所以線程之間信號(hào)阻塞集合是獨(dú)有的).
線程id等...
多線程與多進(jìn)程在多任務(wù)處理中的優(yōu)缺點(diǎn):
多線程優(yōu)點(diǎn):
1. 線程間通信非常靈活(可以通過(guò)全局?jǐn)?shù)據(jù), 函數(shù)傳參, 包括進(jìn)程間通信方式實(shí)現(xiàn)線程間通信)
2. 線程創(chuàng)建與銷(xiāo)毀成本更低(資源大多共享的, 除了獨(dú)立的信息之外, 共享的信息不需要重新另創(chuàng)建)
3. 線程間切換調(diào)度成本稍低(多數(shù)數(shù)據(jù)共享不需要切換調(diào)度)
多進(jìn)程優(yōu)點(diǎn):
1. 獨(dú)立性高,穩(wěn)定性強(qiáng)?(比如某個(gè)線程收到異常退出信號(hào)(或者調(diào)用了exit接口), 此時(shí)這個(gè)進(jìn)程的所有線程都將退出, 信號(hào)是所有線程共享的, 但是如果是多進(jìn)程就只退出異常的進(jìn)程其他進(jìn)程照常運(yùn)行. 所以穩(wěn)定性要求高的場(chǎng)景使用多進(jìn)程, 比如大型的網(wǎng)絡(luò)通信服務(wù)器, 除此之外為了便捷使用多線程)
共同優(yōu)點(diǎn):
cpu密集型程序(程序中大部分是cpu數(shù)據(jù)運(yùn)算)和io密集型程序(程序中大部分是io操作)使用多進(jìn)程或多線程的多執(zhí)行流處理充分利用資源效率更高.
線程控制:
Linux通過(guò)線程庫(kù)中的各種庫(kù)函數(shù)進(jìn)行線程控制.
線程創(chuàng)建:
int pthread_create(? pthread_t* tid,? pthread_attr_t* atrr,? void*(*thread_routine)(void* arg),? void* arg)
pthread_t* tid? :? 使傳入的tid實(shí)參獲取線程id. 后續(xù)通過(guò)這個(gè)tid操作線程.(線程的操作句柄)
pthread_attr_t* atrr? :? 用于設(shè)置線程屬性,通常置NULL.
void*(*thread_routine)(void* arg) :? 線程入口函數(shù),線程要進(jìn)行的函數(shù).
void* arg: 傳遞給線程入口函數(shù)的參數(shù)(若要傳多個(gè)參數(shù), 可以組成一個(gè)結(jié)構(gòu)體把結(jié)構(gòu)體傳入.)
返回值: 成功返回0? ,? 失敗返回非零值.
編譯鏈接的時(shí)候需要加: -l+庫(kù)名(為了跨平臺(tái)性也可以不加-l)? ?如下
實(shí)現(xiàn):
運(yùn)行這個(gè)程序后可以通過(guò)下面的指令觀察兩個(gè)線程的信息(也可以在-L前加個(gè)l查看狀態(tài)):? ?5423就是主線程main的tid也是所有線程的pid.? 5424就是創(chuàng)造的第二個(gè)線程的tid.
線程終止:
線程進(jìn)行的函數(shù)運(yùn)行結(jié)束了, 這個(gè)線程就終止(退出)了.
1. 線程入口函數(shù) return ?(?main主函數(shù)return則退出了進(jìn)程,? 所有線程都終止,和下面不同?)
2. void pthread_exit(void* retval)? 接口 , 沒(méi)有返回值, 通過(guò)傳入的參數(shù)獲取線程退出的返回值.(不需要?jiǎng)t置NULL).? 哪個(gè)線程調(diào)用了這個(gè)接口該線程就退出? (和return不同的是, 如果線程入口函數(shù)調(diào)用了另一個(gè)函數(shù), 那個(gè)函數(shù)中有這個(gè)exit接口則線程直接退出了. 如果沒(méi)有,運(yùn)行完調(diào)用函數(shù)返回到入口函數(shù)再運(yùn)行到入口函數(shù)的return才退出.)
3.int pthread_cancel(pthread_t tid)? 任意位置退出指定線程.? 主線程調(diào)用pthread_cancel(pthread_self() )函數(shù), 或pthread_exit(NULL)??則主線程的狀態(tài)變更成為Z, 其他線程不受影響
線程等待:
默認(rèn)情況下, 一個(gè)線程退出如果不等待也會(huì)造成資源泄露, 所以需要等待指定線程的退出, 獲取這個(gè)線程的退出返回值, 從而釋放資源.? 有時(shí)候不僅僅是為了防止資源泄漏等待, 是必須等到某個(gè)線程處理完得到結(jié)果或者是必須等某個(gè)線程退出才能往下運(yùn)行時(shí)等待.
線程等待接口: int pthread_join(pthread_t tid , void** retval)? 是個(gè)阻塞等待接口.
tid: 等待指定的線程退出.
retval:? 用于接收線程退出返回值. 通常定義一個(gè)void* retval 然后傳入 &retval 作實(shí)參.(如果定義void** retval直接傳retval會(huì)發(fā)成解引用野指針的問(wèn)題. )不需要?jiǎng)t置NULL
但是,當(dāng)我們不關(guān)心一個(gè)線程的返回值的時(shí)候,又不需要等待現(xiàn)成推出才能往下運(yùn)行,這時(shí)候等待會(huì)導(dǎo)致性能降低, 在這種場(chǎng)景之下,等待就不合適了,但是不等待又會(huì)資源泄露基于這個(gè)需求就有了線程分離
?
線程分離:
線程有很多屬性, 其中有一個(gè)叫做分離屬性, 分離屬性默認(rèn)值-JOINABLE, 表示線程退出之后不會(huì)自動(dòng)釋放資源 , 需要被等待, 如果將線程的分離屬性設(shè)置為其他值-DETACH,這時(shí)候則線程退出后之后將不需要被等待,而是直接釋放資源, 因?yàn)榫€程一旦設(shè)置了分離屬性,則退出后自動(dòng)釋放資源,則等待將毫無(wú)意義,所以設(shè)置了分離屬性的線程是不能被等待. 需要等待的線程則不會(huì)設(shè)置線程分離
int pthread_detach( pthread_t?tid) 接口將指定線程分離屬性設(shè)置為detach. ( 通常在一個(gè)線程接口自己內(nèi)部剛開(kāi)始第一行就使用pthread_detach( pthread_self() ))
不想等待某個(gè)線程且不需要它的返回值則將這個(gè)線程分離.
線程安全(的問(wèn)題):
多線程同時(shí)修改同一個(gè)臨界資源可能會(huì)造成數(shù)據(jù)的二義性. 所以需要實(shí)現(xiàn)線程安全保證多線程對(duì)同一個(gè)臨界資源的的訪問(wèn)操作是安全的.
實(shí)現(xiàn)線程安全的方法: 同步與互斥.
互斥(通過(guò) 互斥鎖 實(shí)現(xiàn)): 保證執(zhí)行流在同一時(shí)間對(duì)臨界資源的唯一訪問(wèn).
同步(通過(guò) 條件變量, 信號(hào)量 實(shí)現(xiàn)): 通過(guò)一些規(guī)則(判斷條件)實(shí)現(xiàn)線程對(duì)資源獲取的秩序合理.
*互斥鎖:
互斥鎖的本質(zhì)是一個(gè) 0/1 計(jì)數(shù)器, 主要用于標(biāo)記臨界資源的訪問(wèn)狀態(tài). 0不可訪問(wèn),1可訪問(wèn).
互斥鎖操作: 訪問(wèn)資源之前加鎖(加不上鎖則阻塞,因?yàn)橘Y源還沒(méi)有解鎖), 訪問(wèn)資源完畢則解鎖.??
互斥鎖實(shí)現(xiàn)互斥, 本質(zhì)上自己也是個(gè)臨界資源
同一個(gè)資源所有線程訪問(wèn)的時(shí)候加的是同一把鎖. 不同的鎖則加了沒(méi)有意義.
為了保證互斥鎖自身的操作是安全的, 互斥鎖內(nèi)部的操作是原子操作.
接口流程:
在互斥鎖變量mutex加解鎖之間的代碼是受保護(hù)的安全是臨界區(qū).
?火車(chē)站買(mǎi)票示例:
上述示例中出問(wèn)題的原因在于, 沒(méi)有互斥鎖保護(hù)臨界資源就會(huì):在搶票操作的1ms時(shí)間內(nèi), 時(shí)間片給到另外的黃牛, 此時(shí)第一個(gè)進(jìn)入搶最后一場(chǎng)票的黃牛還沒(méi)完成搶票操作,票數(shù)還為1, 第二第三個(gè)黃牛運(yùn)行一看還有票就也進(jìn)行了搶票操作. 就導(dǎo)致了票數(shù)不正常的情況.? 則此時(shí)我們需要對(duì)票數(shù)這個(gè)資源進(jìn)行互斥鎖操作:
上述程序中, 發(fā)現(xiàn)都是同一個(gè)黃牛在搶票, 這是因?yàn)榛コ怄i只能保證安全操作,無(wú)法保證合理.
因?yàn)樵诩油赕i之后, 第一個(gè)進(jìn)入搶票的黃牛搶完票再解鎖, 此時(shí)因?yàn)闀r(shí)間還在他手上他又馬上運(yùn)行到加鎖搶票的過(guò)程. 然后如此往復(fù)其他黃牛每次有時(shí)間片想加鎖都失敗然后阻塞了.解鎖的時(shí)候時(shí)間片還在原來(lái)?yè)屍钡狞S牛手上,就導(dǎo)致了搶票不合理.? (只是互斥的不合理,不是下面說(shuō)的死鎖哈) 下面的同學(xué)吃飯初始做飯加入了條件變量的同步操作就可以合理的不同的同學(xué)吃飯不同的廚師做飯.
*死鎖:?
程序流程流程無(wú)法繼續(xù)運(yùn)行, 卡死的情況叫死鎖.
產(chǎn)生原因: 由于對(duì)鎖資源爭(zhēng)搶順序不當(dāng)所致.
導(dǎo)致死鎖的四個(gè)必要條件?( 四個(gè)條件都發(fā)生則產(chǎn)生死鎖 ):?
1.互斥條件: 一個(gè)線程加了鎖, 別人不能再加.
2.不可剝奪條件: 我加的鎖別人不能解.
(前兩個(gè)條件是是加互斥鎖之后必然的,所以看是否死鎖得看后兩個(gè)是否發(fā)生)
3.請(qǐng)求與保持條件: 加A鎖后請(qǐng)求B鎖,B鎖請(qǐng)求不到(因?yàn)锽鎖已被加鎖)而不釋放A鎖.
4.環(huán)路等待條件: 加A鎖請(qǐng)求B鎖,對(duì)方已經(jīng)加B鎖請(qǐng)求A鎖.?
綜上理解: 因?yàn)?/strong>規(guī)則是一個(gè)資源只能被一個(gè)線程(1號(hào)線程)加鎖, 且自己加的鎖別人不能解. 然后1號(hào)線程加了一個(gè)資源A的鎖后想給另一個(gè)資源B加自己的鎖, 而另一個(gè)資源B已經(jīng)被另一個(gè)線程(2號(hào)線程)加了2號(hào)自己的鎖 1號(hào)線程就加不了也解不了, 而且2號(hào)線程同時(shí)也想給1號(hào)線程加了鎖的A資源加鎖. 因?yàn)?號(hào)線程得不到B鎖就不釋放A鎖二號(hào)就得不到A鎖, 2號(hào)線程得不到A鎖也不釋放B鎖. 就導(dǎo)致了死鎖( 例子: 哲學(xué)家吃飯問(wèn)題, 哲學(xué)家坐一圈圓桌吃飯, 每個(gè)哲學(xué)家只有一只筷子, 每個(gè)都想要旁邊的人的另一只筷子 ,? 而每個(gè)人得不到另一只筷子吃不到飯就不把自己的筷子給別人導(dǎo)致了死鎖.所以條件1,2理解就是一個(gè)筷子只能同時(shí)被一個(gè)人用, 且不能搶被人的筷子)
對(duì)同一資源加(解)鎖順序不一致導(dǎo)致了環(huán)路等待條件, 阻塞加鎖導(dǎo)致了請(qǐng)求與保持條件. 所以預(yù)防死鎖就得保證加解鎖順序一致, 使用非阻塞加鎖.
避免死鎖方案: 銀行家算法,?銀行家算法的思想在于將系統(tǒng)運(yùn)行分為兩種狀態(tài):安全/非安全,有可能出現(xiàn)風(fēng)險(xiǎn)的都屬于非安全, 安全狀態(tài)則系統(tǒng)中一定無(wú)死鎖進(jìn)程(思想:查看資源請(qǐng)求表,哪個(gè)線程要請(qǐng)求哪個(gè)鎖,根據(jù)所有資源表和已分配資源表判斷,這個(gè)鎖分配給線程是否有可能造成環(huán)路等待(可能造成則不安全),不安全則不予分配.) 等算法...
死鎖的處理辦法:
鴕鳥(niǎo)策略? 對(duì)可能出現(xiàn)的問(wèn)題采取無(wú)視態(tài)度,前提是出現(xiàn)概率很低
預(yù)防策略 破壞死鎖產(chǎn)生的必要條件
避免策略 銀行家算法,分配資源前進(jìn)行風(fēng)險(xiǎn)判斷,避免風(fēng)險(xiǎn)的發(fā)生
檢測(cè)與解除死鎖 分配資源時(shí)不采取措施,但是必須提供死鎖的檢測(cè)與解除手段
*同步:
概念: 通過(guò)一些條件判斷保證執(zhí)行流對(duì)資源獲取的秩序合理.?
即a線程達(dá)到某些條件時(shí)喚醒b線程. 然后自己再陷入加鎖阻塞狀態(tài),等b線程達(dá)到喚醒自己的條件又b又喚醒a(bǔ)線程,如此往復(fù),就實(shí)現(xiàn)了同步.
實(shí)現(xiàn)方式: 條件變量, 信號(hào)量. (信號(hào)量也能實(shí)現(xiàn)互斥)
*條件變量:
例子:
除了等待join直接傳mutex.
加解鎖, 初始化, 等待,?喚醒, 銷(xiāo)毀傳 &mutex, &cond, 初始化和等待加個(gè)NULL.
條件變量和互斥鎖實(shí)現(xiàn)同步與互斥的學(xué)生吃飯廚師做飯問(wèn)題:
*所以注意事項(xiàng)就是:
1.是否滿(mǎn)足需要阻塞條件的判斷應(yīng)該使用循環(huán)操作!!!!
2.多種角色線程等待應(yīng)該分開(kāi)等待,分開(kāi)喚醒防止喚醒角色錯(cuò)誤多種角色定義多個(gè)條件變量.
?
設(shè)計(jì)模式 (多線程的應(yīng)用): 生產(chǎn)者與消費(fèi)者模型
設(shè)計(jì)模式是大佬們針對(duì)典型應(yīng)用場(chǎng)景設(shè)計(jì)的解決方案. 生產(chǎn)者與消費(fèi)者模型就是針對(duì)有大量數(shù)據(jù)產(chǎn)生及處理的場(chǎng)景的設(shè)計(jì)模式, 下面說(shuō)的單例模式也是一種設(shè)計(jì)模式(針對(duì)的是一個(gè)類(lèi)只能實(shí)例化一個(gè)對(duì)象,提供一個(gè)訪問(wèn)接口,一個(gè)資源在內(nèi)存中只能有一份的場(chǎng)景), 以后遇到某些典型的場(chǎng)景就可以使用大佬們搞好的特定設(shè)計(jì)模式解.
生產(chǎn)者與消費(fèi)者模型特點(diǎn): 1.?解耦合(生產(chǎn)和處理分開(kāi),生產(chǎn)線程負(fù)責(zé)生產(chǎn)處理線程負(fù)責(zé)處理, 處理需更多時(shí)間和資源, 多創(chuàng)建幾個(gè)處理線程) 2. 支持忙先不均(生產(chǎn)線程與處理線程并不直接交互, 生產(chǎn)線程生產(chǎn)的要處理的數(shù)據(jù)先放入一個(gè)數(shù)據(jù)緩沖隊(duì)列, 處理線程空閑的話(huà)就查看這個(gè)緩沖任務(wù)隊(duì)列, 有任務(wù)則取出處理.) 3.支持并發(fā)(多個(gè)生產(chǎn)處理線程訪問(wèn)同一個(gè)任務(wù)隊(duì)列, 所以這個(gè)數(shù)據(jù)緩沖隊(duì)列必須保證線程安全同一時(shí)間只有一個(gè)線程對(duì)隊(duì)列操作.)
條件變量和互斥鎖實(shí)現(xiàn)生產(chǎn)者與消費(fèi)者模型: 兩種角色的線程負(fù)責(zé)入隊(duì)(生產(chǎn))和出隊(duì)(處理),? 和一個(gè)線程提供入隊(duì)出隊(duì)的安全的隊(duì)列.?
注意: 運(yùn)行時(shí)出現(xiàn)打印的入隊(duì) (出隊(duì)) 數(shù)據(jù)個(gè)數(shù)比定義的最大的數(shù)據(jù)個(gè)數(shù)MAXQ多的原因是因?yàn)槿雽?duì)數(shù)據(jù) 打印, 出隊(duì)數(shù)據(jù) 打印這兩個(gè)地方的兩步操作不是原子操作, 運(yùn)行了_push之后數(shù)據(jù)最大了, 然后線程阻塞, 時(shí)間片輪轉(zhuǎn)到別的線程, 等待下一次時(shí)間片搶到空余位置插入數(shù)據(jù)再打印然后繼續(xù)運(yùn)行插入數(shù)據(jù),這就是打印多出數(shù)據(jù)的原因, 打印多了不代表隊(duì)列中的數(shù)據(jù)多了. 如果將生產(chǎn)者或消費(fèi)者中的某一角色線程個(gè)數(shù)比另一角色線程個(gè)數(shù)多好多的時(shí)候就會(huì)出現(xiàn)一對(duì)一如隊(duì)即出隊(duì)交互了的假象
*信號(hào)量(POSIX):
posix標(biāo)準(zhǔn)信號(hào)量:
計(jì)數(shù)器用于線程可以是局部變量通過(guò)傳參使用同一個(gè),或者使用全局變量
計(jì)數(shù)器用于進(jìn)程間,這個(gè)計(jì)數(shù)器是通過(guò)共享內(nèi)存實(shí)現(xiàn)的
systemV標(biāo)準(zhǔn)信號(hào)量: linux內(nèi)核提供的一個(gè)計(jì)數(shù)器
本質(zhì): 一個(gè)計(jì)數(shù)器, 用于實(shí)現(xiàn)進(jìn)程或線程之間的同步與互斥.
p操作: 計(jì)數(shù)減一, 且判斷技術(shù)是否大于等于0 , 大于等于0則返回, 小于0則阻塞.
v操作: 計(jì)數(shù)加一, 且喚醒一個(gè)阻塞的進(jìn)程或線程 (其實(shí)sem_post喚醒多個(gè)阻塞進(jìn)程或線程,但是真正獲取到資源的只有一個(gè), 其他的沒(méi)有資源又會(huì)陷入阻塞).
信號(hào)量實(shí)現(xiàn)同步: 通過(guò)隊(duì)資源數(shù)量進(jìn)行計(jì)數(shù), 獲取資源之前進(jìn)行p操作, 產(chǎn)生資源之后進(jìn)行v操作. 通過(guò)這種方式實(shí)現(xiàn)對(duì)資源的合理獲取.
信號(hào)量實(shí)現(xiàn)互斥: 計(jì)數(shù)器初始值為1 ,訪問(wèn)資源前進(jìn)行p操作,?訪問(wèn)完畢進(jìn)行v操作, 實(shí)現(xiàn)類(lèi)似加解鎖的操作.(真正使用中不需要用信號(hào)實(shí)現(xiàn)鎖, 都是用定義好的mutex互斥鎖)
信號(hào)量實(shí)現(xiàn)的生產(chǎn)者與消費(fèi)者模型:
這里運(yùn)行結(jié)果和上面條件變量與互斥鎖實(shí)現(xiàn)的一樣, 打印的數(shù)據(jù)個(gè)數(shù)有誤,原因也是因?yàn)開(kāi)push (_pop)操作和打印操作不是原子操作.
條件變量與信號(hào)量實(shí)現(xiàn)同步上的區(qū)別:
1.本質(zhì)上的不同,信號(hào)量是個(gè)計(jì)數(shù)器,條件變量沒(méi)有計(jì)數(shù)器,因此條件變量的資源訪問(wèn)合理性需要用戶(hù)自己進(jìn)行,但是信號(hào)量可以通過(guò)自身計(jì)數(shù)完成。
⒉.條件變量需要搭配互斥鎖一起使用,而信號(hào)量不需要
其他一些鎖:
*線程池的簡(jiǎn)單實(shí)現(xiàn):
線程池其實(shí)就是一堆(一個(gè)或多個(gè))線程進(jìn)行任務(wù)處理. 針對(duì)有大量任務(wù)需要處理的場(chǎng)景.
上面的生產(chǎn)者消費(fèi)者模型思想其實(shí)就是多線程進(jìn)行任務(wù)處理的思想 , 線程池則可以說(shuō)是多線程任務(wù)處理的具體應(yīng)用. 所以類(lèi)似的, 線程池的實(shí)現(xiàn)思想就是一堆創(chuàng)建好的線程和線程安全的任務(wù)隊(duì)列. 有任務(wù)進(jìn)入線程池中,就會(huì)分配一個(gè)線程處理.? 線程池和來(lái)一個(gè)任務(wù)就創(chuàng)建一個(gè)線程處理比的優(yōu)點(diǎn)是: 1. 節(jié)省了任務(wù)處理過(guò)程中線程創(chuàng)建和銷(xiāo)毀的時(shí)間成本? ? 2. 線程池中的線程和任務(wù)節(jié)點(diǎn)數(shù)量都有最大限制, 避免資源耗盡風(fēng)險(xiǎn).
為了降低線程池的耦合度, 在給出任務(wù)進(jìn)入線程池時(shí)應(yīng)同時(shí)給出任務(wù)的處理方法.( 通過(guò)函數(shù)指針 ),所以給進(jìn)任務(wù)隊(duì)列里的任務(wù)就不只是要處理的數(shù)據(jù)了, 得加上數(shù)據(jù)對(duì)應(yīng)的解決方法, 兩者合并為一個(gè)taskfun類(lèi)傳入任務(wù)隊(duì)列里. 線程池負(fù)責(zé)將任務(wù)入隊(duì)和從隊(duì)中取出任務(wù)并處理.
實(shí)現(xiàn):?
*線程安全的單例模式:
單例模式也是一種設(shè)計(jì)模式, 針對(duì)一個(gè)類(lèi)只能實(shí)例化一個(gè)對(duì)象, 提供一個(gè)訪問(wèn)接口的場(chǎng)景(一個(gè)資源在內(nèi)存中只能有一份的場(chǎng)景)
目的是: 1.節(jié)省空間 2.防止數(shù)據(jù)二義性
兩種實(shí)現(xiàn)方式:
餓漢(資源全部提前加載完畢, 用的時(shí)候可以直接用, 以空間換時(shí)間)
template<class T> class singleton{ public://單例模式只有一個(gè)類(lèi)外能訪問(wèn)的接口,其他構(gòu)造,拷貝構(gòu)造,賦值重載都是私有的steatic T* Getlenstance(){return &data;} private:static T data;//靜態(tài)成員屬于全局, 程序運(yùn)行前就加載好了.singleton(){} //構(gòu)造函數(shù)私有化, 無(wú)法在類(lèi)外實(shí)例化對(duì)象.static singleton _mysingleton;//類(lèi)內(nèi)初始化. };懶漢(資源用的時(shí)候才加載,不用不需要加載. 延遲加載,用的地方更多)?
template<class T> class singleton{ public://volatile關(guān)鍵字防止編譯器過(guò)度優(yōu)化//類(lèi)外初始化靜態(tài)成員時(shí):T* singleton::data=NULL//不使用volatile被編譯器優(yōu)化后會(huì)一直使用寄存器中的NULL.volatile static T* getlenstance(){if(data==NULL){ //加鎖前二次檢查,提高效率._mutex.lock();//多線程可能同時(shí)訪問(wèn),為了線程安全加鎖if(data==NULL){data=new T();//申請(qǐng)調(diào)用時(shí)才加載_mutex.unlock();}}return data;} private:volatile static T* data; //靜態(tài)指針, 申請(qǐng)使用時(shí)才加載變量.static std::mutex _mutex;singleton(){} //構(gòu)造函數(shù)初始化 };總結(jié)
以上是生活随笔為你收集整理的Linux: 多线程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: CentOS 安装SVN以及可视化管理工
- 下一篇: linux c编译 utf-8,在Lin