整理下幾種常用的處理多線程的類缀棍,并將該類對(duì)應(yīng)的常用方法整理下,采用簡(jiǎn)單直觀的例子展示匪凉。
1捌肴、NSThread
NSThread的創(chuàng)建方法有三種:
-
init
方法蹬叭,需要start
啟動(dòng) -
detachNewThreadSelector
方法,自動(dòng)啟動(dòng) -
performSelectorInBackground
方法状知,自動(dòng)啟動(dòng)
init方法
NSThread *threadInit = [[NSThread alloc] initWithTarget:self selector:@selector(thread_initMethod:) object:@"initMethod"];
[threadInit start];
- (void)thread_initMethod:(NSObject *)object
{
NSLog(@"init方法 --> object:%@, %@",object, [NSThread currentThread]);
}
輸出結(jié)果:
init方法 --> object:initMethod, <NSThread: 0x60000027b180>{number = 3, name = (null)}
是在子線程中完成秽五。
detachNewThreadSelector方法
[NSThread detachNewThreadSelector:@selector(thread_detch:) toTarget:self withObject:@"detachNewThread"];
- (void)thread_detch:(NSObject *)object
{
NSLog(@"detach方法 --> object:%@, %@",object, [NSThread currentThread]);
}
輸出結(jié)果:
detach方法 --> object:detachNewThread, <NSThread: 0x60000027b0c0>{number = 4, name = (null)}
是在子線程中完成。
performSelectorInBackground方法
[self performSelectorInBackground:@selector(thread_perform:) withObject:@"performSelectorInBackground"];
- (void)thread_perform:(NSObject *)object
{
NSLog(@"perform方法--> object:%@, %@",object, [NSThread currentThread]);
}
輸出結(jié)果:
perform方法--> object:performSelectorInBackground, <NSThread: 0x60000007a2c0>{number = 5, name = (null)}
是在子線程中完成饥悴。
2坦喘、GCD
GCD中的基本概念:
- 任務(wù):執(zhí)行的代碼塊
- 隊(duì)列:存放任務(wù)的地方
- 同步/異步:執(zhí)行的方式
GCD中的三種隊(duì)列類型:
-
The main queue:與主線程功能相同盲再。實(shí)際上,提交至main queue(主隊(duì)列)的任務(wù)會(huì)在主線程中執(zhí)行瓣铣。main queue可以調(diào)用
dispatch_get_main_queue()
來獲得答朋。因?yàn)閙ain queue是與主線程相關(guān)的,所以這是一個(gè)串行隊(duì)列棠笑。 -
Global queues:全局隊(duì)列是并發(fā)隊(duì)列梦碗,并由整個(gè)進(jìn)程共享。進(jìn)程中存在四個(gè)全局隊(duì)列:高腐晾、中(默認(rèn))叉弦、低丐一、后臺(tái)四個(gè)優(yōu)先級(jí)隊(duì)列藻糖。可以調(diào)用
dispatch_get_global_queue
函數(shù)傳入優(yōu)先級(jí)來訪問隊(duì)列库车。 -
其他隊(duì)列: 其他隊(duì)列是用函數(shù)
dispatch_queue_create
創(chuàng)建的隊(duì)列巨柒。創(chuàng)建隊(duì)列,第一個(gè)參數(shù)是表示debug用的柠衍,Apple建議我們使用倒置域名來命名隊(duì)列洋满;第二個(gè)參數(shù)表示隊(duì)列類型:串行(DISPATCH_QUEUE_SERIAL)或者并發(fā)(DISPATCH_QUEUE_CONCURRENT)。
串行同步
dispatch_queue_t serialQueue = dispatch_queue_create("com.multithreading.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"串行同步:%i --> %@",i, [NSThread currentThread]);
}
});
dispatch_sync(serialQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"串行同步:%i --> %@",i, [NSThread currentThread]);
}
});
dispatch_sync(serialQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"串行同步:%i --> %@",i, [NSThread currentThread]);
}
});
輸出結(jié)果:
串行同步:0 --> <NSThread: 0x600000072d40>{number = 1, name = main}
串行同步:1 --> <NSThread: 0x600000072d40>{number = 1, name = main}
串行同步:2 --> <NSThread: 0x600000072d40>{number = 1, name = main}
串行同步:0 --> <NSThread: 0x60400007ee80>{number = 1, name = main}
串行同步:1 --> <NSThread: 0x60400007ee80>{number = 1, name = main}
串行同步:2 --> <NSThread: 0x60400007ee80>{number = 1, name = main}
串行同步:0 --> <NSThread: 0x60400007ee80>{number = 1, name = main}
串行同步:1 --> <NSThread: 0x60400007ee80>{number = 1, name = main}
串行同步:2 --> <NSThread: 0x60400007ee80>{number = 1, name = main}
都是在主線程中完成珍坊,沒有開辟新線程牺勾。
串行異步
dispatch_queue_t serialQueue = dispatch_queue_create("com.multithreading.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"串行異步:%i --> %@",i, [NSThread currentThread]);
}
});
dispatch_async(serialQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"串行異步:%i --> %@",i, [NSThread currentThread]);
}
});
dispatch_async(serialQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"串行異步:%i --> %@",i, [NSThread currentThread]);
}
});
輸出結(jié)果:
串行異步:0 --> <NSThread: 0x604000467900>{number = 3, name = (null)}
串行異步:1 --> <NSThread: 0x604000467900>{number = 3, name = (null)}
串行異步:2 --> <NSThread: 0x604000467900>{number = 3, name = (null)}
串行異步:0 --> <NSThread: 0x604000467900>{number = 3, name = (null)}
串行異步:1 --> <NSThread: 0x604000467900>{number = 3, name = (null)}
串行異步:2 --> <NSThread: 0x604000467900>{number = 3, name = (null)}
串行異步:0 --> <NSThread: 0x604000467900>{number = 3, name = (null)}
串行異步:1 --> <NSThread: 0x604000467900>{number = 3, name = (null)}
串行異步:2 --> <NSThread: 0x604000467900>{number = 3, name = (null)}
開辟了一個(gè)新線程。
并發(fā)同步
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.multithreading.concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(concurrentQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"并發(fā)同步:%i --> %@",i, [NSThread currentThread]);
}
});
dispatch_sync(concurrentQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"并發(fā)同步:%i --> %@",i, [NSThread currentThread]);
}
});
dispatch_sync(concurrentQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"并發(fā)同步:%i --> %@",i, [NSThread currentThread]);
}
});
輸出結(jié)果:
并發(fā)同步:0 --> <NSThread: 0x60000007c980>{number = 1, name = main}
并發(fā)同步:1 --> <NSThread: 0x60000007c980>{number = 1, name = main}
并發(fā)同步:2 --> <NSThread: 0x60000007c980>{number = 1, name = main}
并發(fā)同步:0 --> <NSThread: 0x60000007c980>{number = 1, name = main}
并發(fā)同步:1 --> <NSThread: 0x60000007c980>{number = 1, name = main}
并發(fā)同步:2 --> <NSThread: 0x60000007c980>{number = 1, name = main}
并發(fā)同步:0 --> <NSThread: 0x60000007c980>{number = 1, name = main}
并發(fā)同步:1 --> <NSThread: 0x60000007c980>{number = 1, name = main}
并發(fā)同步:2 --> <NSThread: 0x60000007c980>{number = 1, name = main}
在主線程中完成阵漏,沒有開辟新線程驻民。
并發(fā)異步
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.multithreading.concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"并發(fā)異步:%i --> %@",i, [NSThread currentThread]);
}
});
dispatch_async(concurrentQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"并發(fā)異步:%i --> %@",i, [NSThread currentThread]);
}
});
dispatch_async(concurrentQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"并發(fā)異步:%i --> %@",i, [NSThread currentThread]);
}
});
輸出結(jié)果:
并發(fā)異步:0 --> <NSThread: 0x600000277480>{number = 4, name = (null)}
并發(fā)異步:0 --> <NSThread: 0x604000465f80>{number = 3, name = (null)}
并發(fā)異步:0 --> <NSThread: 0x604000466140>{number = 5, name = (null)}
并發(fā)異步:1 --> <NSThread: 0x600000277480>{number = 4, name = (null)}
并發(fā)異步:1 --> <NSThread: 0x604000465f80>{number = 3, name = (null)}
并發(fā)異步:1 --> <NSThread: 0x604000466140>{number = 5, name = (null)}
并發(fā)異步:2 --> <NSThread: 0x600000277480>{number = 4, name = (null)}
并發(fā)異步:2 --> <NSThread: 0x604000465f80>{number = 3, name = (null)}
并發(fā)異步:2 --> <NSThread: 0x604000466140>{number = 5, name = (null)}
本例子中開了三條新線程。
主線程同步
- (void)mainQueueSync
{
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"任務(wù)1:%@",[NSThread currentThread]);
});
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"任務(wù)2:%@",[NSThread currentThread]);
});
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"任務(wù)3:%@",[NSThread currentThread]);
});
}
輸出結(jié)果:
主線程鎖死履怯,崩潰
原因:主隊(duì)列同步會(huì)先執(zhí)行任務(wù)1回还,當(dāng)前主線程正在執(zhí)行mainQueueSync
方法,造成執(zhí)行任務(wù)1等待mainQueueSync
方法結(jié)束叹洲,mainQueueSync
方法等待任務(wù)1~3任務(wù)結(jié)束柠硕。
線程通訊
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"做一些費(fèi)時(shí)的東西");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"主線程刷新UI");
});
});
輸出結(jié)果:
做一些費(fèi)時(shí)的東西
主線程刷新UI
柵欄
dispatch_queue_t queue = dispatch_queue_create("com.multithreading.fence", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務(wù)1:%i --> %@",i, [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務(wù)2:%i --> %@",i, [NSThread currentThread]);
}
});
dispatch_barrier_async(queue, ^{
NSLog(@"------并發(fā)異步執(zhí)行柵欄分割------");
});
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務(wù)3:%i --> %@",i, [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務(wù)4:%i --> %@",i, [NSThread currentThread]);
}
});
輸出結(jié)果:
任務(wù)2:0 --> <NSThread: 0x604000069940>{number = 4, name = (null)}
任務(wù)1:0 --> <NSThread: 0x60000026b780>{number = 3, name = (null)}
任務(wù)2:1 --> <NSThread: 0x604000069940>{number = 4, name = (null)}
任務(wù)1:1 --> <NSThread: 0x60000026b780>{number = 3, name = (null)}
任務(wù)2:2 --> <NSThread: 0x604000069940>{number = 4, name = (null)}
任務(wù)1:2 --> <NSThread: 0x60000026b780>{number = 3, name = (null)}
------并發(fā)異步執(zhí)行柵欄分割------
任務(wù)4:0 --> <NSThread: 0x604000069940>{number = 4, name = (null)}
任務(wù)3:0 --> <NSThread: 0x60000026b780>{number = 3, name = (null)}
任務(wù)4:1 --> <NSThread: 0x604000069940>{number = 4, name = (null)}
任務(wù)3:1 --> <NSThread: 0x60000026b780>{number = 3, name = (null)}
任務(wù)4:2 --> <NSThread: 0x604000069940>{number = 4, name = (null)}
任務(wù)3:2 --> <NSThread: 0x60000026b780>{number = 3, name = (null)}
異步是沒有順序的,但是添加了柵欄运提,可以將任務(wù)分為兩部分蝗柔,先做第一部分完畢后,才會(huì)做第二部分民泵。
隊(duì)列組
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.multithreading.group", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務(wù)1:%i --> %@",i,[NSThread currentThread]);
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務(wù)2:%i --> %@",i,[NSThread currentThread]);
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務(wù)3:%i --> %@",i,[NSThread currentThread]);
}
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"任務(wù)完了癣丧,回到主線程刷新UI");
});
輸出結(jié)果:
任務(wù)1:0 --> <NSThread: 0x60400027a080>{number = 3, name = (null)}
任務(wù)3:0 --> <NSThread: 0x604000279fc0>{number = 5, name = (null)}
任務(wù)2:0 --> <NSThread: 0x60400027a0c0>{number = 4, name = (null)}
任務(wù)1:1 --> <NSThread: 0x60400027a080>{number = 3, name = (null)}
任務(wù)2:1 --> <NSThread: 0x60400027a0c0>{number = 4, name = (null)}
任務(wù)3:1 --> <NSThread: 0x604000279fc0>{number = 5, name = (null)}
任務(wù)1:2 --> <NSThread: 0x60400027a080>{number = 3, name = (null)}
任務(wù)2:2 --> <NSThread: 0x60400027a0c0>{number = 4, name = (null)}
任務(wù)3:2 --> <NSThread: 0x604000279fc0>{number = 5, name = (null)}
任務(wù)完了,回到主線程刷新UI
開辟了三條線程洪灯,在任務(wù)1坎缭,2竟痰,3完成后會(huì),回調(diào)dispatch_group_notify
方法掏呼,回到主線程刷新UI坏快。
3、NSOperation
NSOperation的創(chuàng)建:
- 使用NSInvocationOperation子類憎夷,需要start開啟
- 使用NSBlockOperation子類莽鸿,需要start開啟
- 使用繼承NSOperation的子類,需要start開啟
NSInvocationOperation
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation:) object:@"invocation"];
[invocationOperation start];
- (void)invocationOperation:(NSObject *)object
{
NSLog(@"invocation方法:%@",[NSThread currentThread]);
}
輸出結(jié)果:
invocation方法:<NSThread: 0x600000071ec0>{number = 1, name = main}
沒有開新線程拾给。
NSBlockOperation
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block方法:%@",[NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"任務(wù)1:%@",[NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"任務(wù)2:%@",[NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"任務(wù)3:%@",[NSThread currentThread]);
}];
[blockOperation start];
輸出結(jié)果:
block方法:<NSThread: 0x7feaf1701590>{number = 1, name = main}
任務(wù)1:<NSThread: 0x7f839bc21280>{number = 2, name = (null)}
任務(wù)2:<NSThread: 0x7f839be44180>{number = 3, name = (null)}
任務(wù)3:<NSThread: 0x7f839be1f470>{number = 4, name = (null)}
blockOperationWithBlock
方法:主線程實(shí)現(xiàn)祥得,不加入隊(duì)列就不會(huì)開辟新線程
addExecutionBlock
方法:會(huì)開新線程
繼承NSOperation的子類
此例子中繼承NSOperation的子類名為DZROperation
,在.m文件中重寫main
函數(shù)來實(shí)現(xiàn)任務(wù)
// DZROperation.h
#import <Foundation/Foundation.h>
@interface DZROperation : NSOperation
@end
// DZROperation.m
#import "DZROperation.h"
@implementation DZROperation
- (void)main
{
for (int i = 0; i < 3; i++) {
NSLog(@"DZROperation --> %i, %@",i, [NSThread currentThread]);
}
}
@end
DZROperation *operation = [[DZROperation alloc] init];
[operation start];
輸出結(jié)果:
DZROperation --> 0, <NSThread: 0x7fd22ee01ae0>{number = 1, name = main}
DZROperation --> 1, <NSThread: 0x7fd22ee01ae0>{number = 1, name = main}
DZROperation --> 2, <NSThread: 0x7fd22ee01ae0>{number = 1, name = main}
沒有加入隊(duì)列蒋得,是在主線程中實(shí)現(xiàn)级及,未開辟新線程。
NSOperationQueue
NSOperationQueue隊(duì)列類型:
- 主隊(duì)列
- 非主隊(duì)列(串行额衙,并發(fā))
隊(duì)列NSOperationQueue有個(gè)參數(shù)最大并發(fā)數(shù):maxConcurrentOperationCount
- maxConcurrentOperationCount默認(rèn)-1饮焦,直接開啟并發(fā),所以非主隊(duì)列默認(rèn)是開啟并發(fā)
- maxConcurrentOperationCount > 1窍侧,進(jìn)行并發(fā)
- maxConcurrentOperationCount = 1县踢,表示不開線程,是串行
- maxConcurrentOperationCount系統(tǒng)會(huì)限制一個(gè)最大值伟件,所以設(shè)maxConcurrentOperationCount很大也是無意義的
添加自定義NSOperation類到隊(duì)列中
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
DZROperation *operation1 = [[DZROperation alloc] init];
DZROperation *operation2 = [[DZROperation alloc] init];
[queue addOperation:operation1];
[queue addOperation:operation2];
輸出結(jié)果:
DZROperation --> 0, <NSThread: 0x7fc46660c030>{number = 2, name = (null)}
DZROperation --> 0, <NSThread: 0x7fc466605c70>{number = 3, name = (null)}
DZROperation --> 1, <NSThread: 0x7fc46660c030>{number = 2, name = (null)}
DZROperation --> 1, <NSThread: 0x7fc466605c70>{number = 3, name = (null)}
DZROperation --> 2, <NSThread: 0x7fc46660c030>{number = 2, name = (null)}
DZROperation --> 2, <NSThread: 0x7fc466605c70>{number = 3, name = (null)}
此處的NSOperationQueue是默認(rèn)并發(fā)隊(duì)列硼啤,這里開了2個(gè)新線程
直接添加任務(wù)
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 1;
[queue addOperationWithBlock:^{
NSLog(@"添加任務(wù)1:%@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"添加任務(wù)2:%@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"添加任務(wù)3:%@",[NSThread currentThread]);
}];
輸出結(jié)果:
maxConcurrentOperationCount = -1輸出:
添加任務(wù)2:<NSThread: 0x7fa3a2f169f0>{number = 3, name = (null)}
添加任務(wù)1:<NSThread: 0x7fa3a2d09240>{number = 2, name = (null)}
添加任務(wù)3:<NSThread: 0x7fa3a2d0a3d0>{number = 4, name = (null)}maxConcurrentOperationCount = 1輸出:
添加任務(wù)1:<NSThread: 0x7fe65270b6a0>{number = 2, name = (null)}
添加任務(wù)2:<NSThread: 0x7fe65270b6a0>{number = 2, name = (null)}
添加任務(wù)3:<NSThread: 0x7fe65270b6a0>{number = 2, name = (null)}
直接在block中實(shí)現(xiàn)任務(wù)。
operationQueue間通訊
// 主隊(duì)列
NSOperationQueue *mainOperation = [NSOperationQueue mainQueue];
// 并發(fā)隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
NSLog(@"做復(fù)雜操作");
[mainOperation addOperationWithBlock:^{
NSLog(@"刷新UI");
}];
}];
輸出結(jié)果:
做復(fù)雜操作
刷新UI
任務(wù)的執(zhí)行先后設(shè)定 -- 類似GCD的柵欄
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務(wù)1:%d, %@",i, [NSThread currentThread]);
}
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"任務(wù)2:%d, %@",i, [NSThread currentThread]);
}
}];
// 任務(wù)1依賴任務(wù)2
[operation1 addDependency:operation2];
[queue addOperation:operation1];
[queue addOperation:operation2];
輸出結(jié)果:
任務(wù)2:0, <NSThread: 0x7fc82160c390>{number = 2, name = (null)}
任務(wù)2:1, <NSThread: 0x7fc82160c390>{number = 2, name = (null)}
任務(wù)2:2, <NSThread: 0x7fc82160c390>{number = 2, name = (null)}
任務(wù)1:0, <NSThread: 0x7fc82160c390>{number = 2, name = (null)}
任務(wù)1:1, <NSThread: 0x7fc82160c390>{number = 2, name = (null)}
任務(wù)1:2, <NSThread: 0x7fc82160c390>{number = 2, name = (null)}
依賴關(guān)系斧账,任務(wù)1依賴任務(wù)2谴返,任務(wù)2完成后才能完成任務(wù)1。但是不能相互依賴其骄,會(huì)造成鎖死亏镰。
最最后:
詳情demo請(qǐng)轉(zhuǎn)接這里。