提及多線程 , 我們一定不會陌生 , 甚至線程問題已經(jīng)成為iOS 面試必問類型.
iOS 官方提供了 幾種多線程的解決方案 .
NSThread
GCD
NSOperation & NSOperationQueue
今天就 GCD(大調(diào)度中心) 的使用心得 進(jìn)行整理 . 剩下的以后慢慢補(bǔ)全.
基本術(shù)語
關(guān)鍵字 | 英文 | 解釋 |
---|---|---|
串行 | Serial | 同一時間只能執(zhí)行一個任務(wù) |
并發(fā) | Concurrent | 同一時間可以執(zhí)行多個任務(wù) |
平行執(zhí)行 | Parallelism | 并發(fā)執(zhí)行的每一部分是被"同時執(zhí)行"的贫悄。 |
同步 | Synchronous | 按順序執(zhí)行 , 時效性是統(tǒng)一的. |
異步 | Asynchronous | 可同時執(zhí)行 , 時效性不統(tǒng)一 . |
危險區(qū) | Critical Section | 一段代碼不能并發(fā)執(zhí)行.并發(fā)操作共享資源時 , 該資源會損壞. (如數(shù)據(jù)NSMutableArray) |
競態(tài)條件 | Race Condition | 多個進(jìn)程 , 對共享的數(shù)據(jù)進(jìn)行讀或?qū)懙牟僮鲿r , 由進(jìn)程的執(zhí)行順序不同而 導(dǎo)致結(jié)果的不同 . |
死鎖 | DeadLock | 兩個線程進(jìn)入互相等待狀態(tài) , 及對方執(zhí)行完之后我再執(zhí)行 , 從而誰都無法執(zhí)行 , 形成死鎖. |
線程安全 | Thread Safe | 同一段代碼 , 被多個線程同時執(zhí)行時, 不會造成數(shù)據(jù)損壞的代碼 是線程安全的代碼(如NSArray 是, NSMutableArray則不是.) |
環(huán)境切換 | Context Switch | 在多個線程中 , 來回切換執(zhí)行曙寡,稱為環(huán)境切換筑舅。 |
==補(bǔ)充:==
1.競態(tài)條件 : 先檢測后執(zhí)行来累。執(zhí)行依賴于檢測的結(jié)果镊屎,而檢測結(jié)果依賴于多個線程的執(zhí)行時序,而多個線程的執(zhí)行時序通常情況下是不固定不可判斷的扩然,從而導(dǎo)致執(zhí)行結(jié)果出現(xiàn)各種問題珊楼。
例子:對于main線程,如果文件a不存在绪爸,則創(chuàng)建文件a湾碎,但是在判斷文件a不存在之后,Task線程創(chuàng)建了文件a奠货,這時候先前的判斷結(jié)果已經(jīng)失效介褥,(main線程的執(zhí)行依賴了一個錯誤的判斷結(jié)果)此時文件a已經(jīng)存在了,但是main線程還是會繼續(xù)創(chuàng)建文件a递惋,導(dǎo)致Task線程創(chuàng)建的文件a被覆蓋呻顽、文件中的內(nèi)容丟失等等問題.
隊列
GCD提供 dispatch queues 來操作代碼塊。這些隊列管理你提交給GCD的任務(wù)丹墨,并且按照FIFO(first input first output ,先入先出)順序執(zhí)行。
- 串行隊列: 同一時間只能執(zhí)行一個任務(wù) , 沒有進(jìn)入到危險區(qū)的風(fēng)險,從而不能進(jìn)入競態(tài)條件.
- 并發(fā)隊列: 可以同時執(zhí)行多個任務(wù) , 但執(zhí)行結(jié)束時間 , 無法預(yù)測.
-
隊列類型:
- main queue(主線程) :串行隊列. 更新UI界面 , 只能在主線程進(jìn)行.
- Global Dispatch Queues :并發(fā)隊列 , 根據(jù)優(yōu)先級順序 分為(background , low , default , high).(蘋果API 用這些隊列).
- 自定義隊列
- dispatch_async:異步分發(fā)任務(wù) , 使任務(wù)在后臺執(zhí)行嬉愧,從而不阻塞當(dāng)前線程贩挣。 例子: 向主線程分發(fā)任務(wù)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
UIImage *overlayImage = [self faceOverlayImageFromImage:_image];
dispatch_async(dispatch_get_main_queue(), ^{
[self fadeInNewImage:overlayImage];
});
});
- dispatch_after: 延遲執(zhí)行 dispatch_async ,如 延遲執(zhí)行 主隊列
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); // 1
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ // 2
if (!count) {
[self.navigationItem setPrompt:@"Add photos with faces to Googlyify them!"];
} else {
[self.navigationItem setPrompt:nil];
}
});
-
單例的線程安全: 傳統(tǒng)方式創(chuàng)建單例 , 存在線程安全問題 . 如果連續(xù)多次調(diào)用此方法,有這樣一種可能没酣,線程A進(jìn)入if語句王财,在單例創(chuàng)建完成前,發(fā)生環(huán)境切換( context switch),轉(zhuǎn)到線程B裕便,線程B也會進(jìn)入if語句并實例化此單例绒净,然后系統(tǒng)再次環(huán)境切換到線程A,線程A會繼續(xù)執(zhí)行if語句后面的內(nèi)容偿衰,實例化另一個單例挂疆。這顯然就不是單例了.(兩個并發(fā)隊列 , 存在競態(tài)條件)
- dispatch_once 當(dāng)一個線程已經(jīng)在執(zhí)行dispatch_once中的危險區(qū)(critical section),那另一個試圖進(jìn)入該代碼塊的線程將被阻止下翎,直到前一個線程執(zhí)行完畢危險區(qū)中的代碼缤言。
+ (instancetype)sharedManager
{
static PhotoManager *sharedPhotoManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedPhotoManager = [[PhotoManager alloc] init];
sharedPhotoManager->_photosArray = [NSMutableArray array];
});
return sharedPhotoManager;
}
-
dispatch_barrier_async : 可以通過此函數(shù) , 創(chuàng)建讀寫鎖. 當(dāng)它與并發(fā)隊列一起用時,它就像一個串行瓶頸视事。用barriers的API能確保提交的代碼塊是在特定時間指定隊列中唯一被執(zhí)行的胆萧。這意味著先前提交到此隊列中的代碼塊,要在dispatch barrier執(zhí)行前俐东,執(zhí)行完畢跌穗。
-
一般用在自定義的并發(fā)隊列當(dāng)中.
創(chuàng)建讀寫鎖
-
今天暫時分享到此 . 后續(xù)會繼續(xù)更新.