最近在看GCD,閱讀了很多網(wǎng)上的資料,發(fā)現(xiàn)有很多資料寫得非常好摘盆,即全面而又詳細翼雀。自己功力還未到火候,對并發(fā)的理解及研究肯定不如他們孩擂,所以本篇主要收集幾篇關(guān)于并發(fā)的好文章狼渊。
小笨狼漫談多線程:GCD(1)
GCD 掃盲篇
深入理解 GCD(一)
深入理解 GCD(二)
objc中國上并發(fā)編程專題:
并發(fā)編程:API 及挑戰(zhàn)
常見的后臺實踐
底層并發(fā) API
常見的后臺實踐
線程安全類的設(shè)計
關(guān)于線程、多線程类垦。并發(fā)囤锉、并行。
1. 什么是線程护锤、單線程、多線程酿傍?
線程即一個CPU執(zhí)行的命令列為一條無分叉路徑烙懦。只有一條這樣的無分叉的路徑就叫單線程,多條則叫多線程赤炒。
也就是說一個CPU核一次只能執(zhí)行一條CPU的命令氯析,即CPU本來是單線程運作的,那多線程是怎樣實現(xiàn)的呢莺褒?
2.多線程是怎么實現(xiàn)的掩缓?
上下文切換:當只有一個CPU核時,系統(tǒng)會每隔一點時間反復來回切換執(zhí)行路徑遵岩,切換時將各路徑的一些狀態(tài)信息保存在路徑響應(yīng)的內(nèi)存中你辣,等下次切換回來時用。
這個切換時間是如此之快尘执,以至于讓人感覺到一個CPU好像能并列舍哄、同時處理多個線程一樣。而若有多個CPU核誊锭,那則才是真正意義上的并列表悬、同時執(zhí)行。
也就是說單核CPU的多線程是一種多線程的錯覺丧靡,而多核CPU的多線程確實是真正意義上的多線程蟆沫。
3.多線程可能引發(fā)的問題?
爭奪共享資源:比較常見的是多線程讀寫共享資源時可能隱藏這潛在的風險温治,結(jié)果不可靠饭庞。
我們來舉一個關(guān)于爭奪共享資源的簡單示例:用整型變量做計數(shù)器。在程序運行過程中罐盔,我們有兩個并行的線程A和線程B但绕,這兩個線程都可能嘗試修改整數(shù)的值。
那此時存在著什么偶然情況呢?如下圖捏顺,Thread A先開始修改該整數(shù)六孵,它先從內(nèi)存中讀取出該整數(shù)的值17,就在將要給其+1而未加之際幅骄。Thread B也開始修改該整數(shù)了劫窒,它讀取了該整數(shù)的內(nèi)存,因為Thread A還未對其進行+1操作拆座,或者雖在進行中但還未把結(jié)果18寫入該整數(shù)的內(nèi)存主巍。總之就是Thread B讀取到的整數(shù)值也還是17挪凑。接著Thread A和Thread B分別對該整數(shù)完成了+1操作孕索。因為兩者讀取到的初始值都是17,所以最終結(jié)果竟然是18躏碳。這明顯是錯誤的搞旭,對一個數(shù)進行了兩次+1操作,結(jié)果應(yīng)當是19才對菇绵。
因此在多線程下訪問共享資源肄渗,某線程操作某資源時,應(yīng)該排斥限制其他線程訪問該資源咬最。這樣互斥機制應(yīng)運而生翎嫡。為避免出現(xiàn)以上潛在風險,某個線程在操作某資源前得先獲取一個互斥鎖永乌,將其他想訪問該資源而虎視眈眈的線程拒之門外惑申。等這個線程對資源操作完了再釋放掉鎖,這樣別的線程就有機會訪問該共享資源了翅雏。
OC中定義屬性時硝桩,語義修飾詞
atomic
和nonatomic
分別代表“原子性”和“非原子性”。將屬性聲明為atomic
代表在每次訪問該屬性時都會進行隱式的加鎖和解鎖枚荣。最可靠的做法是將所有的屬性都聲明為atomic
碗脊,但是也會為加解鎖付出一定的代價。因此最終選擇是否加鎖橄妆,在于安全和性能的權(quán)衡衙伶。
在資源上加鎖會引發(fā)一定的性能代價,這些代價來源于:
獲取鎖本身就是開銷害碾,而且有時該鎖已經(jīng)被其他線程獲取了矢劲,所以當前線程得等待,此時線程便進入了休眠狀態(tài)慌随。當其他線程釋放掉相關(guān)資源的鎖時芬沉,休眠的線程會得到通知躺同,才進入就緒狀態(tài)獲取鎖。
而且若是獲取資源的鎖后丸逸,對該資源進行的操作比較復雜蹋艺,需要消耗比較長的時間,那其他線程想獲取鎖只好等你執(zhí)行完釋放掉鎖黄刚。比如本來計劃秉性運行的代碼捎谨,但實際上由于共享資源中配置了相關(guān)的鎖,所以同一時間只有一個線程是處于激活狀態(tài)的憔维。
死鎖
優(yōu)先級反轉(zhuǎn)
GCD的多線程
在GCD中涛救,開發(fā)者要做的只是把需要執(zhí)行的任務(wù)追加到合適的Dispatch Queue(隊列)中,開發(fā)者不需要親手管理線程业扒。而一般的所說的任務(wù)是以block形式表現(xiàn)的检吆。
1.關(guān)于隊列:Dispatch Queue
GCD中以執(zhí)行邏輯的不同可分為兩種隊列:串行隊列&并行隊列
- ** 串行隊列:**隊列中的任務(wù),必須等上一個執(zhí)行結(jié)束程储,才能開始一下任務(wù)咧栗。
串行隊列只存在于一個線程中執(zhí)行任務(wù);而若手動創(chuàng)建多個串行隊列虱肄,雖然每個都只有一個線程,但多個串行隊列卻是并行的交煞。這樣仍然能達到并發(fā)的效果咏窿。
- ** 并行隊列:**一個接一個的開始,并不等待上一個任務(wù)執(zhí)行完就開始下一個任務(wù)素征。
所謂“并行隊列”集嵌,就是在一個并行隊列中系統(tǒng)使用多個線程來同時執(zhí)行多個任務(wù),只不過這些線程是由系統(tǒng)來管理的御毅,作為開發(fā)者并不需要關(guān)心根欧。而且到底開啟多少線程,這個也是由系統(tǒng)決定的端蛆。
2.與同步凤粗、異步的區(qū)分
** 同步:將任務(wù)提交給隊列后,等待任務(wù)完成后才返回今豆。
** 異步:將任務(wù)提交給隊列后嫌拣,不等待任務(wù),而是立馬返回呆躲。** 異步必定會另開一個線程异逐,用于處理任務(wù)。**
3.創(chuàng)建隊列
** 自己手動創(chuàng)建:**
// 串行隊列
dispatch_queue_t mySerQueue = dispatch_queue_create("mySerialDispatchQueue.GCDDemo", DISPATCH_QUEUE_SERIAL);
// 并行隊列
dispatch_queue_t myConQueue = dispatch_queue_create("myConcurrentDispatchQueue.GCDDemo",DISPATCH_QUEUE_CONCURRENT);
GCD是C的函數(shù)插掂,沒有自動管理內(nèi)存的技術(shù)灰瞻,所以我們create的東西一定要自己釋放腥例,相應(yīng)的有方法release、retain方法酝润。
** 獲取系統(tǒng)提供的標準隊列:**
Main Dispatch Queue :
在主線程中執(zhí)行的Dispatch Queue燎竖。因為主線程只有一個,所以Main Dispatch Queue為串行隊列袍祖。
Global Dispatch Queue:
存在于系統(tǒng)的底瓣,所有程序都能夠使用的并發(fā)隊列,沒有必要通過create方法手動創(chuàng)建蕉陋,只要用時獲取系統(tǒng)的并行隊列就行了捐凭。
另外,Global Dispatch Queue有4個優(yōu)先級凳鬓。
另外茁肠,通過獲取系統(tǒng)隊列的方法獲取到的Main Dispatch Queue和Global Dispatch Queue執(zhí)行dispatch_retain和dispathc_release不會有任何變化,也不會有任何問題缩举。這也是獲取系統(tǒng)隊列比手動自己創(chuàng)建隊列簡單方便的原因垦梆。