多線程

1.jpeg

本篇文章作為多線程的一個開篇可能會偏向于概念性和基礎(chǔ)性。因為我的計劃是從這篇開始接下來幾篇會去探究下GCD的底層源碼實現(xiàn)。所以有基礎(chǔ)的同學(xué)可以略過這篇文章。

線程和進(jìn)程的定義

線程:
2.png
進(jìn)程:
3.png

線程和進(jìn)程的關(guān)系

4.png

從上面我們知道線程沒有獨立的地址空間,但是我們又聽說過有一個叫TLS(線程的本地存儲)的東西,那這個是什么呢?為什么叫線程的暫存空間呢?這里我摘選《程序員的自我修養(yǎng)》一書中的介紹:

* 線程局部存儲(Thread Local Storage,TLS)。線程局部存儲是某些操作系統(tǒng)為線程單獨提供的私有空間,但通常只具有很有限的容量。

而且這個TLS在后面我們對GCD底層探究的時候會再次看到它,在同步鎖中主要是對SyncData進(jìn)行一些存儲。

多線程

多線程優(yōu)缺點:

我們都知道iOSapp都是單進(jìn)程的(排除那些所謂的web進(jìn)程啥的)。那在這種情況下多線程的出現(xiàn)就更是解決了燃眉之急,因為若是沒有多線程那么所有任務(wù)只能是一個接一個的去等待完成,效率會很低。這就涉及到多線程的意義或者說優(yōu)缺點。如圖:

5.png

在這里也把官方的多線程文檔鏈接發(fā)一下,同時把網(wǎng)上經(jīng)常能看到的一個關(guān)于內(nèi)存的官網(wǎng)圖片也發(fā)下:

6.png
多線程方案:
7.png
多線程原理:

同樣也要明白一個問題就是,多線程并不等于并發(fā)。并不一定是所有任務(wù)一起執(zhí)行。這就涉及到多線程的原理。如圖:

8.png
時間片概念:

所以從上圖我們可以知道多線程也不一定就是并發(fā)(所有任務(wù)一起執(zhí)行)而是在CPU的調(diào)度下在線程間不停切換。這樣就能適當(dāng)?shù)谋苊獗荒硞€線程堵死進(jìn)程,也能適當(dāng)?shù)墓?jié)省資源提高效率。當(dāng)然這里所說的CPU都是以單核為例的。如果是多核那當(dāng)然可以在同一時間調(diào)度處理多個線程。這里就涉及到一個概念-時間片 如圖:

9.png
多線程生命周期:

多線程之間的調(diào)度也是有生命周期的,因為CPU在多個線程間頻繁切換和調(diào)度的時候就涉及到每個線程都必須有各種狀態(tài),比如從線程創(chuàng)建->線程預(yù)備妥當(dāng)->線程被調(diào)度運行->線程死亡。在這過程中還有可能線程被睡眠、加鎖等操作影響從而進(jìn)入阻塞狀態(tài)。等待這一切操作結(jié)束這條線程又要進(jìn)入到預(yù)備調(diào)度運行的過程中。用一張圖表示就如下:

10.png
多線程調(diào)度線程池和飽和策略:

線程調(diào)度池:

11.png
12.png

飽和策略:

13.png

任務(wù)的執(zhí)行速度的影響因素:

我們了解了下調(diào)度線程池飽和策略,那我們就不禁會思考我們?nèi)蝿?wù)執(zhí)行速度會受哪些因素的影響呢?我們大概可以總結(jié)出以下幾點:

14.png

1,CPU的情況
舉個例子:我們?nèi)ツ滩璧曩I奶茶,如果這家店比較小只有一個工作人員和一個窗口來服務(wù),那么相比隔壁一家有三個窗口五個服務(wù)員的比較大的奶茶店這家小的奶茶店在硬件上就比不上隔壁的,這也就是CPU的影響。或者是同一家店你在買奶茶高峰期進(jìn)店買和買奶茶人員稀少的時候進(jìn)店也是不一樣的。同樣也是CPU在不同狀態(tài)下的影響。
2,任務(wù)的復(fù)雜度
舉個例子:我們?nèi)ツ滩璧曩I奶茶,如果兩個服務(wù)員同時給兩個買奶茶的人制作奶茶,一個人要買十杯一個只需要一杯,那很明顯兩個人買完奶茶的任務(wù)的速度也是不一樣的。這就是人物復(fù)雜度的影響。
3,任務(wù)優(yōu)先級
舉個例子:我們還是去奶茶店買奶茶,如果這家店有VIP服務(wù),兩個人進(jìn)店一個是SVIP一個是普通的顧客。在接待上,很明顯SVIP會優(yōu)先,而普通的則只能稍加等待。這就是優(yōu)先級的影響。
4,線程狀態(tài)
舉個例子:我們還是去奶茶店買奶茶,如果這家店有兩個窗口在賣奶茶,兩個人分別同時到達(dá)各自的窗口點單。但是其中一個窗口的工作人員因為突然有急事需要離開兩分鐘,那么這個窗口就必須得暫停服務(wù),另一個窗口的顧客就會先拿到奶茶離開。這就是線程狀態(tài)的影響。

優(yōu)先級翻轉(zhuǎn)(IO VS CPU 優(yōu)先級提升):

當(dāng)我們在執(zhí)行任務(wù)的時候若是一切資源都比較充裕那么任務(wù)會很順利執(zhí)行,但是在遇到資源緊張的時候無論是來自硬件還是任務(wù)的條件都會大大影響任務(wù)執(zhí)行的速度。甚至在某些情況下還會直接在飽和策略里被處理掉,或者直接被一直掛著。這種情況就會出現(xiàn)一個名詞-餓死在這種情況下就出現(xiàn)了另一個名詞-優(yōu)先級翻轉(zhuǎn)

15.png

一般情況下,IO 密集型 (頻繁等待)優(yōu)先級是比 CPU 密集型 (很少等待) 低的。也就是會在某種情況下 因為優(yōu)先級低的緣故或者其他緣故導(dǎo)致線程餓死 也得不到執(zhí)行。這個時候就需要利用一些方法來解決這種情況。所以就需要CPU來調(diào)度分配,將那些 頻繁等待的任務(wù)提升優(yōu)先級等,將資源傾斜一些給那些任務(wù)。但是并不是提升了優(yōu)先級就一定會執(zhí)行因為還有很多其他因素影響。

優(yōu)先級影響因素:

一般我們會認(rèn)為優(yōu)先級的影響因素有三個:

1,用戶指定
2,CPU會根據(jù)進(jìn)入執(zhí)行的等待頻繁度來提高和降低優(yōu)先級
3,當(dāng)有些任務(wù)一直不執(zhí)行的時候也會得到優(yōu)先級提升

以上的三點影響因素是《程序員自我修養(yǎng)》一書中有提到的。

多線程資源搶奪:

資源搶奪是我們在多線程里經(jīng)常聽到的一個名詞,舉個簡單而經(jīng)典的例子:
春運12306售票系統(tǒng)售票,成千上萬個窗口同時售同一趟列車的票。但是票數(shù)是有限的比如10000張,當(dāng)某個窗口售出去一張后,此時服務(wù)器里只剩下999張了,但是第二個窗口和這個窗口去查詢票的時候得到的票數(shù)都是10000,只是第二個窗口出票比第一個窗口慢了一點,這個時候就造成了數(shù)據(jù)不同步,而造成資源搶奪。因為在某一個時刻多個窗口查詢到的票數(shù)一樣但是在出票的時候后臺數(shù)據(jù)卻因為某個窗口出票而有所改變,這個時候其他窗口并不知情,所以剩下的票可能就不足以出售這些窗口下的單。這個時候解決的辦法就是加鎖。

今天我們簡單了解下自旋鎖和互斥鎖的區(qū)別

自旋鎖:
同步鎖,在發(fā)現(xiàn)其他線程執(zhí)行操作的時候,當(dāng)前線程進(jìn)入一個 忙等的狀態(tài),這個狀態(tài)會一直主動詢問,其他線程是否執(zhí)行完畢,若是執(zhí)行完畢則執(zhí)行當(dāng)前線程。自旋鎖性能耗費比較高,適合用于比較短小簡單的任務(wù)。
互斥鎖:
同步鎖,在發(fā)現(xiàn)其他線程執(zhí)行操作的時候,當(dāng)前線程進(jìn)入 休眠狀態(tài),一直等待收到通知喚醒執(zhí)行(需要主動操作)才會執(zhí)行當(dāng)前線程?;コ怄i性能消耗比較小,適用于更多場景。

我們先看看自旋鎖

16.png

其實在我們的開發(fā)過程中會常用到一個東西就是 屬性的修飾符 atomic。我們都知道atomic是原子性 是為多線程開發(fā)準(zhǔn)備的,是默認(rèn)屬性。但是在我們探究OC底層的屬性設(shè)置的時候會發(fā)現(xiàn),atomic 其實就是一個標(biāo)識符,這個標(biāo)識符系統(tǒng)會自動添加一把自旋鎖。而相對的非原子屬性 的nonatomic 卻沒有。這也再次表明nonatomicatomic性能高。我們看看部分代碼:

17.png
18.png
19.png

本文到這里就差不多結(jié)束了,本文主要是針對以下概念的東西來簡單分解,主要目的是為了后面我們用的非常多的GCD篇章做鋪墊。

遇事不決,可問春風(fēng)。站在巨人的肩膀上學(xué)習(xí),如有疏忽或者錯誤的地方還請多多指教。謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容