iOS 多線程
GCD 深入理解:第一部分 作者:Derek Selander
GCD 深入理解:第二部分 作者:Derek Selander
一些常見的誤區
- 并行 vs. 并發
多核設備通過并行來同時執行多個線程;然而,為了使單核設備也能實現這一點,它們必須先運行一個線程,執行一個上下文切換,然后運行另一個線程或進程。這通常發生地足夠快以致給我們并發執行地錯覺,如下圖所示圖片
- 串行 vs. 并發
任務串行執行就是每次只有一個任務被執行,任務并發執行就是在同一時間可以有多個任務被執行。
- 同步 vs. 異步
一個同步函數只在完成了它預定的任務后才返回。一個異步函數,剛好相反,會立即返回,預定的任務會完成但不會等它完成。因此,一個異步函數不會阻塞當前線程去執行下一個函數。
**3種常見方式 **
1.NSThread
2.NSOperation
3. GCD
**dispatch_async **
自定義串行隊列:當你想串行執行后臺任務并追蹤它時就是一個好選擇。這消除了資源爭用,因為你知道一次只有一個任務在執行。注意若你需要來自某個方法的數據,你必須內聯另一個 Block 來找回它或考慮使用 dispatch_sync。主隊列(串行):這是在一個并發隊列上完成任務后更新 UI 的共同選擇。要這樣做,你將在一個 Block 內部編寫另一個 Block 。以及,如果你在主隊列調用 dispatch_async 到主隊列,你能確保這個新任務將在當前方法完成后的某個時間執行。并發隊列:這是在后臺執行非 UI 工作的共同選擇。
dispatch_after
自定義串行隊列:在一個自定義串行隊列上使用 dispatch_after 要小心。你最好堅持使用主隊列。主隊列(串行):是使用 dispatch_after 的好選擇;Xcode 提供了一個不錯的自動完成模版。并發隊列:在并發隊列上使用 dispatch_after 也要小心;你會這樣做就比較罕見。還是在主隊列做這些操作吧
dispatch_once
dispatch_once() 以線程安全的方式執行且僅執行其代碼塊一次。試圖訪問臨界區(即傳遞給 dispatch_once 的代碼)的不同的線程會在臨界區已有一個線程的情況下被阻塞,直到臨界區完成為止。
dispatch barriers
Dispatch barriers 是一組函數,在并發隊列上工作時扮演一個串行式的瓶頸。使用 GCD 的障礙(barrier)API 確保提交的 Block 在那個特定時間上是指定隊列上唯一被執行的條目。這就意味著所有的先于調度障礙提交到隊列的條目必能在這個 Block 執行前完成。當這個 Block 的時機到達,調度障礙執行這個 Block 并確保在那個時間里隊列不會執行任何其它 Block 。一旦完成,隊列就返回到它默認的實現狀態。 GCD 提供了同步和異步兩種障礙函數。下圖顯示了障礙函數對多個異步隊列的影響:![]()
GCD 通過用 dispatch barriers 創建一個讀者寫者鎖 提供了一個優雅的解決方案。解決了 當一個線程調用讀方法 的同時另一個線程調用寫方法.即讀者寫者問題使用場合:自定義并發隊列
dispatch_sync
自定義串行隊列:在這個狀況下要非常小心!如果你正運行在一個隊列并調用 dispatch_sync 放在同一個隊列,那你就百分百地創建了一個死鎖。主隊列(串行):同上面的理由一樣,必須非常小心!這個狀況同樣有潛在的導致死鎖的情況。并發隊列:這才是做同步工作的好選擇,不論是通過調度障礙,或者需要等待一個任務完成才能執行進一步處理的情況。
一個例子 關于單例 讀寫者
// PhotoManager.m
// PhotoFilter//
// Created by A Magical Unicorn on A Sunday Night.// Copyright (c) 2014 Derek Selander. All rights reserved.//@import CoreImage;@import AssetsLibrary;
#import "PhotoManager.h"
@interface PhotoManager ()
@property (nonatomic, strong, readonly) NSMutableArray *photosArray;
@property (nonatomic, strong) dispatch_queue_t concurrentPhotoQueue;
@end
@implementation PhotoManager
// 單例類
+ (instancetype)sharedManager{
static PhotoManager *sharedPhotoManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{
sharedPhotoManager = [[PhotoManager alloc] init];
sharedPhotoManager->_photosArray = [NSMutableArray array];
sharedPhotoManager->_concurrentPhotoQueue = dispatch_queue_create("com.text", DISPATCH_QUEUE_CONCURRENT); }); return sharedPhotoManager;
}
// Getter And Setter
- (NSArray *)photos{
__block NSArray *array;
dispatch_sync(self.concurrentPhotoQueue, ^{
array = [NSArray arrayWithArray:_photosArray];
});
return array;}
- (void)addPhoto:(Photo *)photo{
if (photo) {
dispatch_barrier_async(self.concurrentPhotoQueue, ^{
[_photosArray addObject:photo];
dispatch_async(dispatch_get_main_queue(), ^{
[self postContentAddedNotification];
});;
});
}}
@end
dispatch_group
Dispatch Group 會在整個組的任務都完成時通知你。這些任務可以是同步的,也可以是異步的,即便在不同的隊列也行。而且在整個組的任務都完成時,Dispatch Group 可以用同步的或者異步的方式通知你。因為要監控的任務在不同隊列,那就用一個 dispatch_group_t 的實例來記下這些不同的任務。當組中所有的事件都完成時,GCD 的 API 提供了兩種通知方式。第一種是 dispatch_group_wait ,它會阻塞當前線程,直到組里面所有的任務都完成或者等到某個超時發生。第二種是dispatch_group_notify> 自定義串行隊列:它很適合當一組任務完成時發出通知。主隊列(串行):它也很適合這樣的情況。但如果你要同步地等待所有工作地完成,那你就不應該使用它,因為你不能阻塞主線程。然而,異步模型是一個很有吸引力的能用于在幾個較長任務(例如網絡調用)完成后更新 UI 的方式。并發隊列:它也很適合 Dispatch Group 和完成時通知。