原文: 查看原文 , 原作者寫的非常詳細, 本文摘取了個人認為比較有用且易理解的部分, 碼一遍增強理解, 大家可以移步原文閱讀.
iOS 中可用的4套多線程方案, 并不都建議使用, 視情況而定
- Pthreads
- NSThread
- GCD
- NSOperation & NSOperationQueue
Pthreads
可移植性很強, 但由于基于 c語言框架, 所以并不建議使用.
#import <pthread.h>
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
pthread_t thread;
//創(chuàng)建一個線程并自動執(zhí)行
pthread_create(&thread, NULL, start, NULL);
}
void *start(void *data) {
NSLog(@"%@", [NSThread currentThread]);
return NULL;
}
需要手動管理生命周期
NSThread
經(jīng)過蘋果封裝, 可以直接操控線程對象. 通過[NSThread currentThread]
獲取當前線程類及各種屬性.
- 先創(chuàng)建線程類 再啟動
// 創(chuàng)建
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil];
// 啟動
[thread start];
- 創(chuàng)建并自動啟動
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];
- 使用 NSObject 方法創(chuàng)建并自動啟動
[self performSelectorInBackground:@selector(run:) withObject:nil];
- 判斷某個線程狀態(tài)的屬性
@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
@property (readonly, getter=isCancelled) BOOL cancelled;
- 設(shè)置和獲取線程名字
-(void)setName:(NSString *)n;
-(NSString *)name;
- 獲取線程信息
//獲取當前線程信息
+ (NSThread *)currentThread;
//獲取主線程信息
+ (NSThread *)mainThread;
- 當前線程暫停
+ (void)sleepForTimeInterval:(NSTimeInterval)time;
+ (void)sleepUntilDate:(NSDate *)date;
不夠優(yōu)雅
GCD - 大調(diào)度中心!
自動管理生命周期 , 雖然使用 c語言 但是由于使用了 Block, 所以用起來非常方便.
使用 GCD 只有兩個步驟:
1. 定制任務(wù)
2. 確定想做的事
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block)
將右邊的任務(wù)提交給左邊的隊列執(zhí)行.
同步( sync )
和異步( async )
最大的區(qū)別在于會不會阻塞當前線程, 直到 Block 中的任務(wù)執(zhí)行完畢.
同步( sync )
它會阻塞當前線程并等待 Block 中的任務(wù)執(zhí)行完畢蹦疑,然后當前線程才會繼續(xù)往下運行。
異步( async )
當前線程會直接往下執(zhí)行萨驶,它不會阻塞當前線程歉摧。
串行隊列
放到串行隊列的任務(wù),GCD 會 FIFO(先進先出) 地取出來一個,執(zhí)行一個叁温,然后取下一個再悼,這樣一個一個的執(zhí)行。
并行隊列
放到并行隊列的任務(wù)膝但,GCD 也會 FIFO的取出來冲九,但不同的是,它取出來一個就會放到別的線程跟束,然后再取出來一個又放到另一個的線程莺奸。這樣由于取的動作很快,忽略不計冀宴,看起來灭贷,所有的任務(wù)都是一起執(zhí)行的。不過需要注意花鹅,GCD 會根據(jù)系統(tǒng)資源控制并行的數(shù)量氧腰,所以如果任務(wù)很多,它并不會讓所有任務(wù)同時執(zhí)行刨肃。
注: 無論是串行隊列
還是并行隊列
, 都遵守先進先出 FIFO 原則 , 只是并行隊列允許同時執(zhí)行多個任務(wù).
|同步執(zhí)行 | 異步執(zhí)行
---|--------|-------
串行隊列 | 當前線程古拴,一個一個執(zhí)行 | 其他線程,一個一個執(zhí)行
并行隊列 | 當前線程真友,一個一個執(zhí)行 | 開很多線程黄痪,一起執(zhí)行
創(chuàng)建隊列
- 主隊列 : 用于刷新 UI,任何需要刷新 UI 的工作都要在主隊列執(zhí)行盔然,所以一般耗時的任務(wù)都要放到別的線程執(zhí)行桅打。
dispatch_queue_t queue = ispatch_get_main_queue();
- 自己創(chuàng)建隊列 : 自己可以創(chuàng)建
串行隊列
, 也可以創(chuàng)建并行隊列
。- 第一個參數(shù)是標識符愈案,用于 DEBUG 的時候標識唯一的隊列挺尾,可以為空烦周。
- 第二個參數(shù)用來表示創(chuàng)建的隊列是串行的還是并行的冈敛,傳入 DISPATCH_QUEUE_SERIAL 或 NULL 表示創(chuàng)建串行隊列。傳入 DISPATCH_QUEUE_CONCURRENT 表示創(chuàng)建并行隊列振乏。
//串行隊列
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL);
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL);
//并行隊列
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT);
- 全局并行隊列 : 只要是并行任務(wù)一般都加入到這個隊列恢准。這是系統(tǒng)提供的一個并發(fā)隊列魂挂。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
創(chuàng)建任務(wù)
- 同步任務(wù)( sync ): 會阻塞當前線程
dispatch_sync(<#queue#>, ^{
//code here
NSLog(@"%@", [NSThread currentThread]);
});
- 異步任務(wù)( async ): 不會阻塞
dispatch_async(<#queue#>, ^{
//code here
NSLog(@"%@", [NSThread currentThread]);
});
NSOperation和NSOperationQueue
NSOperation 是蘋果公司對 GCD 的封裝,完全面向?qū)ο竽倏穑允褂闷饋砀美斫狻?大家可以看到 NSOperation
和 NSOperationQueue
分別對應 GCD 的 任務(wù)
和 隊列
涂召。
添加任務(wù)
NSOperation 只是一個抽象類,所以不能封裝任務(wù)敏沉。但它有 2 個子類用于封裝任務(wù)果正。分別是:NSInvocationOperation
和 NSBlockOperation
炎码。創(chuàng)建一個 Operation 后,需要調(diào)用 start
方法來啟動任務(wù)舱卡,它會 默認在當前隊列同步執(zhí)行辅肾。當然你也可以在中途取消一個任務(wù)队萤,只需要調(diào)用其 cancel
方法即可轮锥。
- NSInvocationOperation : 傳入一個方法
//1.創(chuàng)建NSInvocationOperation對象
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
//2.開始執(zhí)行
[operation start];
- NSBlockOperation
//1.創(chuàng)建NSBlockOperation對象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//2.開始任務(wù)
[operation start];
默認會在當前線程執(zhí)行。但是 NSBlockOperation
還有一個方法:addExecutionBlock
: 要尔,通過這個方法可以給 Operation
添加多個執(zhí)行 Block
舍杜。這樣 Operation 中的任務(wù) 會并發(fā)執(zhí)行
,它會在主線程和其它的多個線程
執(zhí)行這些任務(wù)
//1.創(chuàng)建NSBlockOperation對象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//添加多個Block
for (NSInteger i = 0; i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
}];
}
//2.開始任務(wù)
[operation start];
NSOperationQueue
有一個參數(shù) maxConcurrentOperationCount
最大并發(fā)數(shù)赵辕,用來設(shè)置最多可以讓多少個任務(wù)同時執(zhí)行既绩。當你把它設(shè)置為 1 的時候,就是串行.
NSOperationQueue
還有一個添加任務(wù)的方法还惠,- (void)addOperationWithBlock:(void (^)(void))block
; 饲握,這是不是和 GCD 差不多?這樣就可以添加一個任務(wù)到隊列中了蚕键,十分方便救欧。
//1.任務(wù)一:下載圖片
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下載圖片 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//2.任務(wù)二:打水印
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"打水印 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//3.任務(wù)三:上傳圖片
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"上傳圖片 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//4.設(shè)置依賴
[operation2 addDependency:operation1]; //任務(wù)二依賴任務(wù)一
[operation3 addDependency:operation2]; //任務(wù)三依賴任務(wù)二
//5.創(chuàng)建隊列并加入任務(wù)
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];
- 注意:不能添加相互依賴,會死鎖锣光,比如 A依賴B笆怠,B依賴A。
- 可以使用 removeDependency 來解除依賴關(guān)系誊爹。
- 可以在不同的隊列之間依賴蹬刷,反正就是這個依賴是添加到任務(wù)身上的,和隊列沒關(guān)系频丘。
延遲執(zhí)行
- perform
// 3秒后自動調(diào)用self的run:方法办成,并且傳遞參數(shù):@"abc"
[self performSelector:@selector(run:) withObject:@"abc" afterDelay:3];
- GCD
// 創(chuàng)建隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 設(shè)置延時,單位秒
double delay = 3;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), queue, ^{
// 3秒后需要執(zhí)行的任務(wù)
});
- NSTimer
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(run:) userInfo:@"abc" repeats:NO];
從其他線程回到主線程
- NSThread
[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];
- GCD
dispatch_async(dispatch_get_main_queue(), ^{
});
- NSOperationQueue
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
}];