關于多線程,在編程中那是必不可少的,現在我們就好好梳理一下多線程.
在 iOS 中其實目前主要有3套多線程方案,他們分別是:
NSThread
GCD
NSOperation & NSOperationQueue
NSThread
NSThread是經過蘋果封裝后的,并且完全面向對象的。所以你可以直接操控線程對象,非常直觀和方便。
它的生命周期還是需要我們手動管理,所以并不推薦使用
//多線程處理
-(void)manyThreadHandle
{
//方法1
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(makerun:) object:@"1"];
// 啟動
[thread start];
//方法2? 不用手動啟動 自動啟動
[NSThread detachNewThreadWithBlock:^{
NSLog(@"--方法2");
}];
//方法3? 不用手動啟動自動啟動
[NSThread detachNewThreadSelector:@selector(makerun:) toTarget:self withObject:@"2"];
}
-(void)makerun:(NSThread*)thred
{
NSLog(@"多線程--%@",thred);
}
2017-08-30 16:01:24.863 runtimeTest[6579:165898] --方法2
2017-08-30 16:01:24.864 runtimeTest[6579:165897] 多線程--1
2017-08-30 16:01:24.864 runtimeTest[6579:165899] 多線程--2
在NSThread 中海油很多方法和屬性
//取消線程
- (void)cancel;
//啟動線程
- (void)start;
//判斷某個線程的狀態的屬性
@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
@property (readonly, getter=isCancelled) BOOL cancelled;
//設置和獲取線程名字
-(void)setName:(NSString *)n;
-(NSString *)name;
//獲取當前線程信息
+ (NSThread *)currentThread;
//獲取主線程信息
+ (NSThread *)mainThread;
//使當前線程暫停一段時間,或者暫停到某個時刻
+ (void)sleepForTimeInterval:(NSTimeInterval)time;
+ (void)sleepUntilDate:(NSDate *)date;
GCD 方法
GCD 這個名字是不是很霸氣 ,天朝人都懂得.其實指的是Grand Central Dispatch(GCD)
GCD會自動合理地利用更多的CPU內核(比如雙核、四核),最重要的是它會自動管理線程的生命周期(創建線程、調度任務、銷毀線程),完全不需要我們管理,我們只需要告訴干什么就行。同時它使用的也是c語言,不過由于使用了 Block(Swift里叫做閉包),使得使用起來更加方便,而且靈活。這個方法比較實用簡便,強烈推薦.
在GCD中,加入了兩個非常重要的概念:任務和隊列。
任務
任務:即操作,你想要干什么,說白了就是一段代碼,在 GCD 中就是一個 Block,所以添加任務十分方便。任務有兩種執行方式:同步執行和異步執行,主要區別在于會不會阻塞當前線程,直到Block中的任務執行完畢!
同步執行:阻塞當前線程,不會開辟新的線程
異步執行:不會阻塞當前線,程會開辟新的線程
隊列:用于存放任務。一共有兩種隊列,串行隊列和并行隊列。
放到串行隊列的任務,GCD 會FIFO(先進先出)地取出來一個,執行一個,然后取下一個,這樣一個一個的執行。簡單說串行 就是單行車道 ,所有的車(任務)都只能按順序走
放到并行隊列的任務,GCD 也會FIFO的取出來,但不同的是,它取出來一個就會放到別的線程,然后再取出來一個又放到另一個的線程。這樣由于取的動作很快,忽略不計,看起來,所有的任務都是一起執行的。簡單說并行 就是多條車道 ,所有的車(任務)都可以在對應車道上走,和隔壁車道沒什么影響
主隊列? 任何需要刷新 UI 的工作都要在主隊列執行
dispatch_queue_t queue = dispatch_get_main_queue();
自己創建隊列
//串行隊列
dispatch_queue_t queue = dispatch_queue_create("tk22.bournes.testQueue", NULL);
dispatch_queue_t queue = dispatch_queue_create("tk22.bournes.testQueue", DISPATCH_QUEUE_SERIAL);
//并行隊列
dispatch_queue_t queue = dispatch_queue_create("tk22.bournes.testQueue", DISPATCH_QUEUE_CONCURRENT);
//全局并行隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
隊列組
隊列組可以將很多隊列添加到一個組里,這樣做的好處是,當這個組里所有的任務都執行完了,隊列組會通過一個方法通知我們。
//1.創建隊列組
dispatch_group_t group = dispatch_group_create();
//2.創建隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//多次使用隊列組的方法執行任務, 只有異步方法
//執行3次循環
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 3; i++) {
NSLog(@"group-01 - %@", [NSThread currentThread]);
}
});
//主隊列執行8次循環
dispatch_group_async(group, dispatch_get_main_queue(), ^{
for (NSInteger i = 0; i < 8; i++) {
NSLog(@"group-02 - %@", [NSThread currentThread]);
}
});
//執行10次循環
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"group-03 - %@", [NSThread currentThread]);
}
});
//4.都完成后會自動通知
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"完成 - %@", [NSThread currentThread]);
});
//運行結果
2017-08-30 16:21:36.201 runtimeTest[6843:174540] group-01 -{number = 3, name = (null)} ? ? ? ? ?2017-08-30 16:21:36.201 runtimeTest[6843:174555] group-03 -{number = 4, name = (null)} ? ? ? ? 2017-08-30 16:21:36.202 runtimeTest[6843:174555] group-03 -{number = 4, name = (null)} ? ? ? ?2017-08-30 16:21:36.202 runtimeTest[6843:174540] group-01 -{number = 3, name = (null)} ? ? ? ? 2017-08-30 16:21:36.202 runtimeTest[6843:174555] group-03 -{number = 4, name = (null)} ? ? ? ?2017-08-30 16:21:36.202 runtimeTest[6843:174540] group-01 -{number = 3, name = (null)} ? ? ? ? 2017-08-30 16:21:36.202 runtimeTest[6843:174555] group-03 -{number = 4, name = (null)} ? ? ? ?2017-08-30 16:21:36.202 runtimeTest[6843:174555] group-03 -{number = 4, name = (null)} ? ? ? ?2017-08-30 16:21:36.202 runtimeTest[6843:174555] group-03 -{number = 4, name = (null)} ? ? ? ?2017-08-30 16:21:36.203 runtimeTest[6843:174555] group-03 -{number = 4, name = (null)} ? ? ? ?2017-08-30 16:21:36.203 runtimeTest[6843:174555] group-03 -{number = 4, name = (null)} ? ? ? ?2017-08-30 16:21:36.203 runtimeTest[6843:174555] group-03 -{number = 4, name = (null)} ? ? ? ?2017-08-30 16:21:36.203 runtimeTest[6843:174555] group-03 -{number = 4, name = (null)} ? ? ? ?2017-08-30 16:21:36.206 runtimeTest[6843:174502] group-02 -{number = 1, name = main} ? ? ? ? 2017-08-30 16:21:36.207 runtimeTest[6843:174502] group-02 -{number = 1, name = main} ? ? ? ? ?2017-08-30 16:21:36.207 runtimeTest[6843:174502] group-02 -{number = 1, name = main} ? ? ? ? ?2017-08-30 16:21:36.207 runtimeTest[6843:174502] group-02 -{number = 1, name = main} ? ? ? ? ?2017-08-30 16:21:36.207 runtimeTest[6843:174502] group-02 -{number = 1, name = main} ? ? ? ? ?2017-08-30 16:21:36.208 runtimeTest[6843:174502] group-02 -{number = 1, name = main} ? ? ? ? 2017-08-30 16:21:36.208 runtimeTest[6843:174502] group-02 -{number = 1, name = main} ? ? ? ? 2017-08-30 16:21:36.208 runtimeTest[6843:174502] group-02 -{number = 1, name = main} ? ? ? ? ?2017-08-30 16:21:36.209 runtimeTest[6843:174502] 完成 -{number = 1, name = main}
NSOperation和NSOperationQueue
NSOperation 是蘋果公司對 GCD 的封裝,完全面向對象,所以使用起來更好理解,有很多和GCD比較類似
NSOperation 和 NSOperationQueue分別對應 GCD 的任務 和 隊列
NSOperation只是一個抽象類,所以不能封裝任務它有 2 個子類用于封裝任務。分別是:NSInvocationOperation和NSBlockOperation
當你新創建一個 Operation 后,需要手動的調用start方法來啟動任務,它會默認在當前隊列同步執行。當然你也可以在中途取消一個任務,只需要調用其cancel方法即可。
//1.創建NSInvocationOperation對象
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
//2.開始執行
[operation start];
//1.創建NSBlockOperation對象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//添加多個Block
for (NSInteger i = 0; i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
}];
}
//2.開始任務
[operation start];
2017-08-30 16:33:12.868 runtimeTest[7014:179819]{number = 1, name = main}
2017-08-30 16:33:12.869 runtimeTest[7014:179819] 第3次:{number = 1, name = main}
2017-08-30 16:33:12.869 runtimeTest[7014:179819] 第4次:{number = 1, name = main}
2017-08-30 16:33:12.868 runtimeTest[7014:179888] 第1次:{number = 4, name = (null)}
2017-08-30 16:33:12.868 runtimeTest[7014:179892] 第0次:{number = 3, name = (null)}
2017-08-30 16:33:12.869 runtimeTest[7014:179889] 第2次:{number = 5, name = (null)}
根據結果可以知道打印的順序是亂的 這個也符合我們異步的任務執行情況
//1.創建NSBlockOperation對象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//2.開始任務
[operation start];
創建隊列
//主隊列
NSOperationQueue *queue = [NSOperationQueue mainQueue];
//1.創建一個其他隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2.創建NSBlockOperation對象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//3.添加多個Block
for (NSInteger i = 0; i < 2; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
}];
}
//4.隊列添加任務
[queue addOperation:operation];
2017-08-30 16:35:56.163 runtimeTest[7073:181494]{number = 3, name = (null)}
2017-08-30 16:35:56.163 runtimeTest[7073:181537] 第0次:{number = 4, name = (null)}
2017-08-30 16:35:56.163 runtimeTest[7073:181538] 第1次:{number = 5, name = (null)}
NSOperation有一個非常實用的功能,那就是添加依賴 和gcd中的隊列組管理有點類似.有點啰嗦了,就先介紹這些吧,以后有時間會更新.