iOS中的多線程解決方案:
- Pthreads
- NSThread
- GCD
- NSOperation & NSOperationQueue
Pthreads
基于C語言,在類Unix操作系統(tǒng)(Unix、Linux、Mac OS X等)中,都可以使用。
(1)實現(xiàn)必須導(dǎo)入頭文件
#import <pthread.h>
(2)然后創(chuàng)建線程,并執(zhí)行任務(wù)
- (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;
}
由于是C語言函數(shù),所以需要自己管理線程的生命周期(切換、銷毀)
NSThread
Apple封裝之后,面向?qū)ο?,但是仍然需要手動管理生命周期。但可以獲得當前線程的各種屬性,比[NSThread currentThread],用于調(diào)試很方便。
- 創(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];
- performSelector形式創(chuàng)建線程
[self performSelectorInBackground:@selector(run:) withObject:nil];
其他作用
//取消線程
- (void)cancel;
//啟動線程
- (void)start;
//判斷某個線程的狀態(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;
GCD
使用了Block,系統(tǒng)自動管理生命周期。
- 任務(wù):每一個Block就是一個任務(wù)(你想要做什么)。
- 隊列:用于存放任務(wù)。
任務(wù)有兩種執(zhí)行方式:
同步執(zhí)行:會阻塞當前線程,必須等Block完,才能繼續(xù)往下執(zhí)行。
異步執(zhí)行:不會阻塞當前線程,程序不必等待,會直接繼續(xù)執(zhí)行。
隊列也有兩種:
串行隊列:按順序執(zhí)行,即先進入的先執(zhí)行,后進入的后執(zhí)行。
并行隊列:同時執(zhí)行任務(wù),沒有執(zhí)行順序。
注意:異步操作才會開辟新線程。串行隊列按順序執(zhí)行任務(wù),并行隊列同時執(zhí)行多個任務(wù)。
創(chuàng)建隊列
- 主隊列:不會開辟新線程,串行執(zhí)行任務(wù)。
dispatch_queue_t queue = ispatch_get_main_queue();
- 全局隊列:并行隊列。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- 創(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);
創(chuàng)建任務(wù)
- 同步
dispatch_sync(<#queue#>, ^{
//code here
NSLog(@"%@", [NSThread currentThread]);
});
- 異步
dispatch_async(<#queue#>, ^{
//code here
NSLog(@"%@", [NSThread currentThread]);
});
總結(jié)效果:

注意:
異步添加串行隊列,只會開啟一個新線程;
異步和同步?jīng)Q定是否開啟新線程,并行和串行決定任務(wù)的執(zhí)行順序;
隊列組
隊列組可以將很多隊列添加到一個組里,這樣做的好處是,當這個組里所有的任務(wù)都執(zhí)行完了,隊列組會通過一個方法通知我們
//1.創(chuàng)建隊列組
dispatch_group_t group = dispatch_group_create();
//2.創(chuàng)建隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//3.多次使用隊列組的方法執(zhí)行任務(wù), 只有異步方法
//3.1.執(zhí)行3次循環(huán)
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 3; i++) {
NSLog(@"group-01 - %@", [NSThread currentThread]);
}
});
//3.2.主隊列執(zhí)行8次循環(huán)
dispatch_group_async(group, dispatch_get_main_queue(), ^{
for (NSInteger i = 0; i < 8; i++) {
NSLog(@"group-02 - %@", [NSThread currentThread]);
}
});
//3.3.執(zhí)行5次循環(huán)
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 5; i++) {
NSLog(@"group-03 - %@", [NSThread currentThread]);
}
});
//4.都完成后會自動通知
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"完成 - %@", [NSThread currentThread]);
});
NSOperation和NSOperationQueue
NSOperation 是蘋果公司對 GCD 的封裝,完全面向?qū)ο?,所以使用起來更好理解?大家可以看到NSOperation 和 NSOperationQueue 分別對應(yīng) GCD 的 任務(wù) 和 隊列 。
1.將要執(zhí)行的任務(wù)封裝到一個 NSOperation 對象中。
2.將此任務(wù)添加到一個 NSOperationQueue 對象中。
添加任務(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];
之前說過這樣的任務(wù),默認會在當前線程執(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];
創(chuàng)建隊列
看過上面的內(nèi)容就知道,我們可以調(diào)用一個 NSOperation 對象的 start() 方法來啟動這個任務(wù),但是這樣做他們默認是 同步執(zhí)行 的。就算是 addExecutionBlock 方法,也會在當前線程和其他線程中執(zhí)行,也就是說還是會占用當前線程。這時就要用到隊列 NSOperationQueue了。而且,按類型來說的話一共有兩種類型:主隊列、其他隊列。只要添加到隊列,會自動調(diào)用任務(wù)的 start() 方法。
- 主隊列
NSOperationQueue *queue = [NSOperationQueue mainQueue];
- 其他隊列
//1.創(chuàng)建一個其他隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2.創(chuàng)建NSBlockOperation對象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//3.添加多個Block
for (NSInteger i = 0; i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
}];
}
//4.隊列添加任務(wù)
[queue addOperation:operation];
NSOperationQueue有一個參數(shù)maxConcurrentOperationCount(最大并發(fā)數(shù)),用來設(shè)置最多可以讓多少個任務(wù)同時執(zhí)行。當你把它設(shè)置為 1 的時候,就是串行。添加任務(wù)的方法:
- (void)addOperationWithBlock:(void (^)(void))block;添加依賴
//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];
注意:
(1)不能添加相互依賴,會死鎖,比如 A依賴B,B依賴A。
(2)可以使用 removeDependency 來解除依賴關(guān)系。
(3)可以在不同的隊列之間依賴,反正就是這個依賴是添加到任務(wù)身上的,和隊列沒關(guān)系。
其他方法
- NSOperation
BOOL executing; //判斷任務(wù)是否正在執(zhí)行
BOOL finished; //判斷任務(wù)是否完成
void (^completionBlock)(void); //用來設(shè)置完成后需要執(zhí)行的操作
- (void)cancel; //取消任務(wù)
- (void)waitUntilFinished; //阻塞當前線程直到此任務(wù)執(zhí)行完畢
- NSOperationQueue
NSUInteger operationCount; //獲取隊列的任務(wù)數(shù)
- (void)cancelAllOperations; //取消隊列中所有的任務(wù)
- (void)waitUntilAllOperationsAreFinished; //阻塞當前線程直到此隊列中的所有任務(wù)執(zhí)行完畢
[queue setSuspended:YES]; // 暫停queue
[queue setSuspended:NO]; // 繼續(xù)queue
其他
線程同步
- 同步鎖,即資源競爭:多條線程同時訪問同一資源,造成的數(shù)據(jù)安全問題,通過加鎖保證每次只有一個線程訪問資源。
@synchronized(self) {
//需要執(zhí)行的代碼塊
}
- 同步執(zhí)行:把多個線程都要執(zhí)行此段代碼添加到同一個串行隊列,這樣就實現(xiàn)了線程同步的概念。
//GCD
//需要一個全局變量queue,要讓所有線程的這個操作都加到一個queue中
dispatch_sync(queue, ^{
NSInteger ticket = lastTicket;
[NSThread sleepForTimeInterval:0.1];
NSLog(@"%ld - %@",ticket, [NSThread currentThread]);
ticket -= 1;
lastTicket = ticket;
});
//NSOperation & NSOperationQueue
//重點:1. 全局的 NSOperationQueue, 所有的操作添加到同一個queue中
// 2. 設(shè)置 queue 的 maxConcurrentOperationCount 為 1
// 3. 如果后續(xù)操作需要Block中的結(jié)果,就需要調(diào)用每個操作的waitUntilFinished,阻塞當前線程,一直等到當前操作完成,才允許執(zhí)行后面的。waitUntilFinished 要在添加到隊列之后!
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSInteger ticket = lastTicket;
[NSThread sleepForTimeInterval:1];
NSLog(@"%ld - %@",ticket, [NSThread currentThread]);
ticket -= 1;
lastTicket = ticket;
}];
[queue addOperation:operation];
[operation waitUntilFinished];
//后續(xù)要做的事
延遲執(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];
單例
@interface Tool : NSObject <NSCopying>
+ (instancetype)sharedTool;
@end
@implementation Tool
static id _instance;
+ (instancetype)sharedTool {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[Tool alloc] init];
});
return _instance;
}
@end
從其他線程返回主線程
- NSThread
[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];
- GCD
dispatch_async(dispatch_get_main_queue(), ^{
});
- NSOperationQueue
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
}];