- 本文主要探索
NSThread
,GCD
,NSOperation
这三种实现多线程的方式;
NSThread
- NSthread是苹果官方提供面向对象的线程操作技术,是对thread的上层封装,比较偏向于底层,简单方便,可以直接操作线程对象,使用频率较少;
NSThread的初始化创建
【第一种】:alloc方法
- (void)viewDidLoad {
[super viewDidLoad];
//第一种方式: alloc 需手动开启
YYThread *thread = [[YYThread alloc]initWithTarget:self selector:@selector(task:) object:@"thread_name_01"];
[thread start];
}
- (void)task:(NSObject *)obj{
NSLog(@"%@ - %@", obj, [NSThread currentThread]);
}
- YYThread自定义线程继承自NSThread;
- 会创建子线程执行任务(task:方法);
- 需要手动的启动,手动调用start方法;
【第二种】:detachNewThreadSelector类方法
- (void)viewDidLoad {
[super viewDidLoad];
[NSThread detachNewThreadSelector:@selector(task:) toTarget:self withObject:@"thread_name_02"];
}
- (void)task:(NSObject *)obj{
NSLog(@"%@ - %@", obj, [NSThread currentThread]);
}
- detachNewThreadSelector类方法会生成子线程执行任务(task:方法);
【第三种】:performSelector方法
- 调用performSelectorOnMainThread方法,若当前线程是主线程,会在主线程中立刻执行selector任务方法;
- (void)viewDidLoad {
[super viewDidLoad];
//当前线程为主线程
[self performSelectorOnMainThread:@selector(task_perform:) withObject:@"thread_name_04" waitUntilDone:YES];
}
- (void)task_perform:(NSObject *)obj{
NSLog(@"%@ - %@", obj, [NSThread currentThread]);
}
- 调用performSelectorOnMainThread方法,
若当前线程是子线程,waitUntilDone = YES时,会阻塞当前子线程
,当主线程执行完selector任务方法时,解除子线程的阻塞,继续执行子线程中的任务;
- (void)viewDidLoad {
[super viewDidLoad];
YYThread *thread = [[YYThread alloc]initWithTarget:self selector:@selector(task:) object:@"thread_name_01"];
[thread start];
}
- (void)task:(NSObject *)obj{
NSLog(@"%@ - %@", obj, [NSThread currentThread]);
//当前线程为子线程
[self performSelectorOnMainThread:@selector(task_perform:) withObject:@"thread_name_04" waitUntilDone:YES];
}
- (void)task_perform:(NSObject *)obj{
NSLog(@"%@ - %@", obj, [NSThread currentThread]);
}
- task方法中的执行完打印之后,
子线程阻塞
,当主线程执行完task_perform之后,子线程解除阻塞
,然后继续执行,子线程任务执行完,子线程销毁;
- 调用performSelectorOnMainThread方法,
若当前线程是子线程,waitUntilDone = NO时,不会阻塞当前子线程
,子线程中任务继续执行,selector任务方法在主线程中执行;
- 将上面的代码,waitUntilDone入参改成NO,LLDB结果如下:
- task方法中的执行完打印之后,
子线程不会阻塞
,继续执行,子线程任务执行完成后直接销毁;
- 主线程执行task_perform方法;
- 总结:
- 若当前线程为子线程,performSelectorOnMainThread:withObject:waitUntilDone方法中的参数waitUntilDone决定了是否阻塞当前子线程,即同步/异步;
- 调用performSelectorInBackground方法,开启新的线程在后台执行test方法任务;
- 调用performSelector:onThread方法,在指定的线程执行任务;
NSThread常见方法和属性
-
[NSThread currentThread]
获取当前线程信息
-
NSThread sleepUntilDate:
当前线程休眠到指定时间
-
NSThread sleepForTimeInterval:
当前线程休眠多长时间
-
[NSThread exit]
当前线程退出
-
[NSThread mainThread]
获取主线程
-
[NSThread isMainThread]
判断当前线程是否时主线程
-
[NSThread mainThread]
获取主线程
-
thread.isExecuting
线程是否在执行
-
thread.isCancelled
线程是否被取消
-
thread.isFinished
线程是否完成
-
thread.isMainThread
线程是否是主线程
-
thread.threadPriority
线程的优先级,取值范围0.0-1.0,默认优先级0.5,1.0表示最高优先级,优先级高,CPU调度的频率高
GCD
dispatch_after
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"aaaaa");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"3333");
});
}
dispatch_once
- 保证在App运行期间,block中的代码只会执行一次;
//创建单例
+ (instancetype)sharedInstance{
static dispatch_once_t onceToken;
static id instance;
dispatch_once(&onceToken, ^{
instance = [[self alloc]init];
});
return instance;
}
dispatch_apply
- 将耗时任务,以指定的次数,追加到队列中,并等待任务全部执行结束;
- 使用场景:for循环中有大量耗时任务;
【第一种解决方案】:在for循环中耗时任务,都异步开辟子线程去执行
- (void)viewDidLoad {
[super viewDidLoad];
//在for循环中每一次任务都放到子线程中执行
for (int i = 0; i < 1000; i++) {
dispatch_async(dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT), ^{
[NSThread sleepForTimeInterval:1]; // 模拟耗时操作
NSLog(@"%d -- %@", i, [NSThread currentThread]);
});
}
}
- 从打印结果来看,1000个耗时任务能很快执行完,但是开辟了将近70个子线程来执行耗时任务,这样会耗费大量的资源,容易导致App的崩溃,所以第一种方案不可取;
【第二种解决方案】:使用dispatch_apply函数
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT), ^{
dispatch_apply(1000, dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT), ^(size_t index) {
[NSThread sleepForTimeInterval:1];//模拟耗时操作
NSLog(@"%zu -- %@", index, [NSThread currentThread]);
});
});
}
- 使用dispatch_apply函数将block任务循环1000次,加入并发队列中;
- 从打印结果来看,1000个耗时任务执行完成需要耗费很长一段时间,但是只开启了五六个子线程循环执行耗时任务,不会耗费大量资源;
- 现在定义一个数组,然后使用dispatch_apply函数循环打印数组中元素,代码实现如下所示:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"-- begin --");
NSArray *arr = @[@"a", @"b", @"c", @"d", @"e"];
dispatch_queue_t queue = dispatch_queue_create("com.jarypan.gcdsummary", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(arr.count, queue, ^(size_t index) {
[NSThread sleepForTimeInterval:2];
NSLog(@"index = %zu, str = %@ -- %@", index, arr[index], [NSThread currentThread]);
});
NSLog(@"-- end --");
}
- 使用dispatch_apply函数往并发队列中循环添加了5个任务;
- 开启多个子线程去执行任务,
在执行任务时会阻塞主线程,只有当dispatch_apply添加到并发队列中的任务全部执行完,才会解除主线程的阻塞
,主线程才能继续执行;
- 为了不阻塞主线程的执行,我们将dispatch_apply函数放到异步子线程中去执行,代码改造之后如下:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"-- begin --");
NSArray *arr = @[@"a", @"b", @"c", @"d", @"e"];
dispatch_queue_t queue = dispatch_queue_create("com.jarypan.gcdsummary", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"current thread -- %@", [NSThread currentThread]);
dispatch_apply(arr.count, queue, ^(size_t index) {
[NSThread sleepForTimeInterval:2];
NSLog(@"index = %zu, str = %@ -- %@", index, arr[index], [NSThread currentThread]);
});
NSLog(@" current thread - end ");
});
NSLog(@"-- end --");
}
- 可以看到dispatch_apply函数会
阻塞当前子线程的继续执行
,当追加到并发队列中任务执行完成,才会解除子线程的阻塞
- 任务循环添加到并发队列,但是任务执行却是串行执行,并没有实现我们想要的并发执行,(为什么?),如果
将上面的自定义并发队列改成全局队列
就可以实现任务的并发执行
;
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"-- begin --");
NSArray *arr = @[@"a", @"b", @"c", @"d", @"e"];
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"current thread -- %@", [NSThread currentThread]);
dispatch_apply(arr.count, queue, ^(size_t index) {
[NSThread sleepForTimeInterval:2];
NSLog(@"index = %zu, str = %@ -- %@", index, arr[index], [NSThread currentThread]);
});
NSLog(@" current thread - end ");
});
NSLog(@"-- end --");
}
dispatch_apply会造成死锁的场景
第一种:在主线程使用dispatch_apply往主队列中添加任务
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_apply(10, dispatch_get_main_queue(), ^(size_t index) {
NSLog(@"%zu", index);
});
}
-
dispatch_apply
往主队列中添加block任务,而viewDidLoad
已在主线中执行,block任务会等待viewDidLoad执行完再执行;
- 由于
dispatch_apply
会阻塞主线程的继续执行,viewDidLoad
会等待block任务执行完才会继续执行完成;造成viewDidLoad
与block任务的相互等待形成死锁;
-
dispatch_apply
可以看成dispatch_sync
;
第二种情况:使用dispatch_apply往异步串行队列中追加任务
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{//block1
dispatch_apply(10, queue, ^(size_t index) {//block2
NSLog(@"%zu", index);
});
});
}
dispatch_group调度组
-
dispatch_group_async
,dispatch_group_enter
,dispatch_group_leave
,dispatch_group_notify
与dispatch_group_wait
配合使用;
- 使用场景:多个异步网络请求,回调统一处理;
- (void)viewDidLoad {
[super viewDidLoad];
self.bookId = @"11186718404761703yw";
//创建队列组
self.group = dispatch_group_create();
//创建队列
self.queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(self.group, self.queue, ^{
[self loadBookView];
});
dispatch_group_async(self.group, self.queue, ^{
[self loadBookEvalute];
});
dispatch_group_async(self.group, self.queue, ^{
[self loadBookRecommandList];
});
long timeout = dispatch_group_wait(self.group, dispatch_time(DISPATCH_TIME_NOW, 1 *NSEC_PER_SEC));
NSLog(@"timeout = %ld", timeout);
if (timeout == 0) {
NSLog(@"按时完成任务");
}else{
NSLog(@"超时");
}
dispatch_group_notify(self.group, self.queue, ^{
NSLog(@"同步所有异步请求");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"刷新UI");
});
});
}
- (void)loadBookView{
///请求一
dispatch_group_enter(self.group);
[YYRequestBookApi requestBookViewWithBookId:self.bookId success:^(id _Nonnull response) {
dispatch_group_leave(self.group);
} fail:^(NSString * _Nonnull error) {
dispatch_group_leave(self.group);
}];
}
- (void)loadBookEvalute{
///请求二
dispatch_group_enter(self.group);
[YYRequestBookApi requestBookEvaluteWithBookId:self.bookId success:^(id _Nonnull response) {
dispatch_group_leave(self.group);
} fail:^(NSString * _Nonnull error) {
dispatch_group_leave(self.group);
}];
}
- (void)loadBookRecommandList{
///请求三
dispatch_group_enter(self.group);
[YYRequestBookApi requestBookRecommentWithBookId:self.bookId success:^(id _Nonnull response) {
dispatch_group_leave(self.group);
} fail:^(NSString * _Nonnull error) {
dispatch_group_leave(self.group);
}];
}
-
dispatch_group_enter
与dispatch_group_leave
需要配对使用;
-
dispatch_group_wait
设置调度组等待的超时时间(即等多久);
- 当三个网络请求的回调数据都回来之后,在
dispatch_group_notify
函数中做统一处理;
dispatch_barrier栅栏函数
- 可实现并发队列中的任务,按照顺序执行;
- dispatch_barrier分为两种分别为
dispatch_barrier_sync
与dispatch_barrier_async
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_queue_create("com.lyy.queue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@" start ");
dispatch_async(queue, ^{
sleep(5);
NSLog(@"---1--- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"---2--- %@",[NSThread currentThread]);
});
dispatch_barrier_sync(queue, ^{
NSLog(@" 栅栏函数 -- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"---3--- %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"---4--- %@",[NSThread currentThread]);
});
NSLog(@" end ");
}
- 并发队列中追加四个任务,任务1是耗时任务,子线程休眠5秒;
dispatch_barrier_sync栅栏同步
添加在四个任务的正中间;
- 可以看出
dispatch_barrier_sync
会阻塞主线程的继续执行,当其block执行完成时,主线程解除阻塞继续执行;
-
dispatch_barrier_sync
栅栏同步将并发队列的任务执行分割开,必须等任务1(耗时任务)与任务2都执行完毕时才会执行栅栏同步函数,栅栏同步函数执行完成之后再执行任务3,任务4。
- 将上面的
dispatch_barrier_sync
栅栏同步改成dispatch_barrier_async
栅栏异步,调试结果如下:
- 可以看出
dispatch_barrier_async
栅栏异步不会阻塞主线程,end
会立即执行且会开辟子线程,在子线程中执行;
-
dispatch_barrier_async
栅栏异步将并发队列的任务执行分割开,必须等任务1(耗时任务)与任务2都执行完毕时才会执行栅栏异步函数,栅栏异步函数执行完成之后再执行任务3,任务4。
dispatch_semaphore_t信号量
- 主要涉及三个函数如下:
-
dispatch_semaphore_create()
创建信号量,并设定一个初始信号量值;
-
dispatch_semaphore_signal()
发送信号量,信号量值+1;
-
dispatch_semaphore_wait()
等待信号量,信号量值-1;
-
当信号量值 < 0时,会阻塞当前线程的继续执行
,当信号量>=0时,当前线程解除阻塞
,会继续执行接下来的逻辑;
应用场景一:配合dispatch_group实现多个异步网络请求,回调统一处理,实现如下:
- (void)viewDidLoad {
[super viewDidLoad];
self.bookId = @"11186718404761703yw";
self.semphore = dispatch_semaphore_create(0);
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^{
[self loadBookView];
});
dispatch_group_async(group, queue, ^{
[self loadBookEvalute];
});
dispatch_group_async(group, queue, ^{
[self loadBookRecommandList];
});
dispatch_group_notify(group, queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"刷新UI");
});
});
}
- (void)loadBookView{
///请求一
[YYRequestBookApi requestBookViewWithBookId:self.bookId success:^(id _Nonnull response) {
dispatch_semaphore_signal(self.semphore);
} fail:^(NSString * _Nonnull error) {
dispatch_semaphore_signal(self.semphore);
}];
dispatch_semaphore_wait(self.semphore, DISPATCH_TIME_FOREVER);
}
- (void)loadBookEvalute{
///请求二
[YYRequestBookApi requestBookEvaluteWithBookId:self.bookId success:^(id _Nonnull response) {
dispatch_semaphore_signal(self.semphore);
} fail:^(NSString * _Nonnull error) {
dispatch_semaphore_signal(self.semphore);
}];
dispatch_semaphore_wait(self.semphore, DISPATCH_TIME_FOREVER);
}
- (void)loadBookRecommandList{
///请求三
[YYRequestBookApi requestBookRecommentWithBookId:self.bookId success:^(id _Nonnull response) {
dispatch_semaphore_signal(self.semphore);
} fail:^(NSString * _Nonnull error) {
dispatch_semaphore_signal(self.semphore);
}];
dispatch_semaphore_wait(self.semphore, DISPATCH_TIME_FOREVER);
}
应用场景二:实现异步并发(串行)队列中的任务,依次顺序执行,如果是队列中的任务是网络请求,也是可以控制网络请求回调的顺序执行
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(1);
NSLog(@"任务1:%@",[NSThread currentThread]);
dispatch_semaphore_signal(sem);//2
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);//1
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(1);
NSLog(@"任务2:%@",[NSThread currentThread]);
dispatch_semaphore_signal(sem);//4
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);//3
dispatch_async(dispatch_get_global_queue(0, 0), ^{//5
sleep(1);
NSLog(@"任务3:%@",[NSThread currentThread]);
});
}
- 信号量的初始值为0,首先执行到1,等待信号量信号量减1,变成-1会阻塞当前主线程的继续执行,所以dispatch_semaphore_wait后面的代码执行不了;
- 第一个
dispatch_async
函数开始执行,当执行到2时,发送信号量,信号量+1变成0,那么主线程解除阻塞继续执行,来到3等待信号量信号量减1,又变成-1,又会阻塞当前主线程,后面的代码不能执行;
- 第二个
dispatch_async
函数开始执行,当执行到4时,发送信号量,信号量+1变成0,那么主线程解除阻塞继续执行,执行第三个dispatch_async
函数;
应用场景三:控制异步并发的数量
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 20; i++) {
dispatch_async(queue, ^{
//等待降低信号量
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task %d -- %@",i,[NSThread currentThread]);
sleep(2);
NSLog(@"complete task %d -- %@",i,[NSThread currentThread]);
//提高信号量
dispatch_semaphore_signal(semaphore);
});
}
}
- 可以看到信号量的初始值,控制了异步任务的并发数,即同一时刻任务执行的数量;
dispatch_source_t
-
dispatch_source_t
主要用于计时操作,计时精准度比NSTimer高,其原因是因为它创建的timer不依赖于RunLoop;
- 常见API方法如下:
- dispatch_source_create: 创建事件源
- dispatch_source_set_event_handler: 设置数据源回调
- dispatch_source_merge_data: 设置事件源数据
- dispatch_source_get_data: 获取事件源数据
- dispatch_resume: 继续
- dispatch_suspend: 挂起
- dispatch_cancle: 取消
- (void)viewDidLoad {
[super viewDidLoad];
//1.创建队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//2.创建timer
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
self.timer = timer;
//3.设置timer首次执行时间,间隔,精确度
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 2.0*NSEC_PER_SEC, 0);
//4.设置timer事件回调
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"GCDTimer");
});
//5.默认是挂起状态,需要手动激活
dispatch_resume(self.timer);
}
NSOperation
- NSOperation是基于GCD之上的更高一层封装,NSOperation需要配合NSOperationQueue来实现多线程;
NSInvocationOperation与NSBlockOperation
-
NSOperation是抽象类
,在实际使用的中需要使用它的两个子类NSInvocationOperation
与NSBlockOperation
;
- (void)viewDidLoad {
[super viewDidLoad];
//创建NSInvocationOperation实例对象 并绑定操作方法
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(task) object:nil];
//start开始执行操作
[operation start];
//创建NSBlockOperation实例对象 在Block中添加任务操作
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:1];//模拟耗时操作
NSLog(@"NSBlockOperation excute - %@", [NSThread currentThread]);
}];
//start开始执行操作
[operation1 start];
}
- (void)task{
NSLog(@"NSInvocationOperation excute - %@",[NSThread currentThread]);
}
- 单独使用NSInvocationOperation与NSBlockOperation时,在主线程中执行任务操作;
- NSBlockOperation还可以调用
addExecutionBlock函数
,给NSBlockOperation添加其他block任务,这些任务在子线程中并发执行
;
- (void)viewDidLoad {
[super viewDidLoad];
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@", [NSThread currentThread]);
}];
[operation addExecutionBlock:^{
[NSThread sleepForTimeInterval:2];
NSLog(@"2---%@", [NSThread currentThread]);
}];
[operation addExecutionBlock:^{
[NSThread sleepForTimeInterval:2];
NSLog(@"3---%@", [NSThread currentThread]);
}];
[operation start];
}
自定义NSOperation
#import <Foundation/Foundation.h>
@interface YYOperation : NSOperation
@end
#import "YYOperation.h"
@implementation YYOperation
//重写main方法
- (void)main{
if (!self.isCancelled) {
[NSThread sleepForTimeInterval:2];
NSLog(@"main --- %@", [NSThread currentThread]);
}
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
YYOperation *operation = [[YYOperation alloc]init];
[operation start];
}
NSOperationQueue的使用
- 其使用步骤如下:
- 1、创建任务:先将需要执行的任务封装到NSOperation对象中;
- 2、创建队列:创建NSOperationQueue;
- 3、将任务加入到队列中:将NSOperation操作对象添加到NSOperationQueue中;
- (void)viewDidLoad {
[super viewDidLoad];
//1.获取主队列
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
//2.创建操作
//使用 NSInvocationOperation 创建操作1
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
//使用 NSInvocationOperation 创建操作2
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task2) object:nil];
//使用 NSBlockOperation 创建操作3
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:2];
NSLog(@"3---%@", [NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
[NSThread sleepForTimeInterval:2];
NSLog(@"4---%@", [NSThread currentThread]);
}];
//3.调用addOperation: 添加所有操作到主队列中
[mainQueue addOperation:op1]; //[op1 start]
[mainQueue addOperation:op2]; //[op2 start]
[mainQueue addOperation:op3]; //[op3 start]
}
- (void)task1{
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@", [NSThread currentThread]);
}
- (void)task2{
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
}
- 创建的操作添加到NSOperationQueue中,
不用再调用start方法来启动执行,是由系统去启动执行操作
;
- 添加到主队列中的操作都是在主线程中,依次执行,由于op3调用addExecutionBlock添加的操作会并发执行;
- 将上面的队列改成自定义队列 NSOperationQueue *queue = [[NSOperationQueue alloc]init],LLDB调试如下:
- 可以看到操作任务添加到自定义队列中,是在子线程中并发执行;
往NSOperationQueue中直接添加block任务
- (void)viewDidLoad {
[super viewDidLoad];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
for (int i = 0; i < 5; i++) {
[queue addOperationWithBlock:^{
NSLog(@"%@---%d", [NSThread currentThread], i);
}];
}
}
- NSOperationQueue调用
addOperationWithBlock
直接往操作队列中追加block任务;
- 由于添加到自定义操作队列,所以所有任务并发执行;
往NSOperationQueue设置并发数
- 在GCD中我们通过信号量
dispatch_semaphore_t
,控制异步并发队列的并发数;
- 在NSOperationQueue中我们直接通过其属性
maxConcurrentOperationCount
,控制自定义NSOperationQueue操作队列的并发数;
- (void)viewDidLoad {
[super viewDidLoad];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.name = @"lyy.queue";
queue.maxConcurrentOperationCount = 2;
for (int i = 0; i < 10; i++) {
[queue addOperationWithBlock:^{ // 一个任务
[NSThread sleepForTimeInterval:2];
NSLog(@"%d-%@",i,[NSThread currentThread]);
}];
}
}
- 当自定义操作队列的最大并发数设置为2时,表明同一时刻只开辟两个子线程执行任务;当最大并发数设置为3时,LLDB调试结果如下:
NSOperation设置依赖
- (void)viewDidLoad {
[super viewDidLoad];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:0.5];
NSLog(@"请求token");
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:0.5];
NSLog(@"拿着token,请求数据1");
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:0.5];
NSLog(@"拿着数据1,请求数据2");
}];
[op2 addDependency:op1];
[op3 addDependency:op2];
[queue addOperations:@[op1,op2,op3] waitUntilFinished:YES];
NSLog(@"执行完了 -- 我要干其他事");
}
- op2依赖于op1,表明只有先执行op1,才能执行op2;
- op3依赖于op2,表明只有先执行op2,才能执行op3;
NSOperation设置优先级
- NSOperation设置优先级只会让CPU有更高的几率调用,不是说设置高就一定全部先执行;
- 常见优先级有如下:从高到低
- NSQualityOfServiceUserInteractive:用户交互级别:最高级别,通常用于响应用户操作的UI处理,如将图像绘制到屏幕上;
- NSQualityOfServiceUserInitiated:用户发起级别:由用户发起的仅次于UI的任务,用户希望立即响应并在此任务完成后进行下一步操作。如远程内容载入;
- NSQualityOfServiceUtility:工具级别:由用户或自动发起,不必要立即响应,不阻止用户交互。通常以一个可见的进度条来标示进度,例如预加载内容、上传或大量文件操作(如媒体导入);
- NSQualityOfServiceBackground:后台级别:通常不是由用户发起、用户不可见的。例如备份、索引、数据同步等;
- NSQualityOfServiceDefault:将从其他来源推测QoS,如果无法推断,将使用 UserInitiated 到 Utility中的一个;
- (void)viewDidLoad {
[super viewDidLoad];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
// sleep(1);
NSLog(@"第一个操作 %d --- %@", i, [NSThread currentThread]);
}
}];
//设置最低优先级
op1.qualityOfService = NSQualityOfServiceBackground;
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"第二个操作 %d --- %@", i, [NSThread currentThread]);
}
}];
//设置最高优先级
op2.qualityOfService = NSQualityOfServiceUserInteractive;
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op1];
[queue addOperation:op2];
}
- 由于op2的优先级比op1优先级高,所以先执行op2;
- 若在op1中加入睡眠一秒的操作,且设置op1的优先级最高,op2优先级最低,代码如下所示:
- (void)viewDidLoad {
[super viewDidLoad];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
sleep(1);
NSLog(@"第一个操作 %d --- %@", i, [NSThread currentThread]);
}
}];
//设置最高优先级
op1.qualityOfService = NSQualityOfServiceUserInteractive;
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"第二个操作 %d --- %@", i, [NSThread currentThread]);
}
}];
//设置最低优先级
op2.qualityOfService = NSQualityOfServiceBackground;
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op1];
[queue addOperation:op2];
}
- 看到即使op1的优先级最高,op2优先级最低,由于op1有阻塞操作,最终op2首先执行,表明NSOperation设置优先级只会让CPU有更高的几率调用,不是说设置高就一定先执行,还要考虑其他因素;
NSOperationQueue实现线程间通信
- (void)viewDidLoad {
[super viewDidLoad];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.name = @"lyy.queue";
[queue addOperationWithBlock:^{
//子线程执行耗时操作
NSLog(@"耗时操作 -- %@", [NSThread currentThread]);
//切换到主线程刷新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"刷新UI -- %@", [NSThread currentThread]);
}];
}];
}