场景: 现在需要执行三个接口(1001/1002/1003) ,三个接口全部执行完成后再执行刷新UI操作.
目录:
- GCD+group 实现以上场景
- GCD+semaphore信号量 实现以上场景
- semaphore信号量讲解
- NSOperation讲解
- 自定义NSOperation
首先写一个模拟网络请求的延时操作的方法和一个主线程刷新UI的方法:
#pragma mark - 模拟网络请求
-(void)requestNetDataWithParams:(NSString *)params andResultBlock:(resultBlock)resultBlock{
float t = 0.5 + arc4random_uniform(20)/10.0;
NSLog(@"开始执行接口:%@", params);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(t * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSString *message = [NSString stringWithFormat:@"接口执行完成:%@",params];
NSLog(@"%@,耗时:%.2fs", message, t);
NSData *data = [message dataUsingEncoding:NSUTF8StringEncoding];
resultBlock(YES, data);
});
}
#pragma mark - 刷新UI
-(void)refreshUI{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"refresh UI on Thread: %@", [NSThread currentThread]);
});
}
实现方式1: GCD+group
-(void)reloadData{
//执行接口 a b c 并行
__weak typeof(self) weakself = self;
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
[weakself requestNetDataWithParams:@"1001" andResultBlock:^(BOOL isOk, NSData *result) {
dispatch_group_leave(group);
}];
});
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
[weakself requestNetDataWithParams:@"1002" andResultBlock:^(BOOL isOk, NSData *result) {
dispatch_group_leave(group);
}];
});
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
[weakself requestNetDataWithParams:@"1003" andResultBlock:^(BOOL isOk, NSData *result) {
dispatch_group_leave(group);
}];
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"接口全部执行完毕!");
[weakself refreshUI];
});
}
/*执行结果
2019-08-16 15:46:51.065570+0800 多线程应用[29706:7605331] 开始执行接口:1001
2019-08-16 15:46:51.065582+0800 多线程应用[29706:7605332] 开始执行接口:1003
2019-08-16 15:46:51.065588+0800 多线程应用[29706:7605334] 开始执行接口:1002
2019-08-16 15:46:51.932222+0800 多线程应用[29706:7605207] 接口执行完成:1001,耗时:0.80s
2019-08-16 15:46:52.382853+0800 多线程应用[29706:7605207] 接口执行完成:1002,耗时:1.20s
2019-08-16 15:46:52.929765+0800 多线程应用[29706:7605207] 接口执行完成:1003,耗时:1.70s
2019-08-16 15:46:52.930066+0800 多线程应用[29706:7605207] 接口全部执行完毕!
2019-08-16 15:46:52.930451+0800 多线程应用[29706:7605207] refresh UI on Thread: {number = 1, name = main}
*/
实现方式2: GCD+信号量
-(void)reloadData{
__weak typeof(self) weakself = self;
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t que = dispatch_get_global_queue(0, 0);
//信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_group_async(group, que, ^{
[weakself requestNetDataWithParams:@"1001" andResultBlock:^(BOOL isOk, NSData *result) {
dispatch_semaphore_signal(semaphore);
}];
});
dispatch_group_async(group, que, ^{
[weakself requestNetDataWithParams:@"1002" andResultBlock:^(BOOL isOk, NSData *result) {
dispatch_semaphore_signal(semaphore);
}];
});
dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(2);
dispatch_group_async(group, que, ^{
[weakself requestNetDataWithParams:@"1003" andResultBlock:^(BOOL isOk, NSData *result) {
dispatch_semaphore_signal(semaphore2);
}];
});
dispatch_group_notify(group, que, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphore2, DISPATCH_TIME_FOREVER);
NSLog(@"接口全部执行完毕!");
[weakself refreshUI];
});
}
/*执行结果
2019-08-16 15:55:58.522111+0800 多线程应用[29787:7613733] 开始执行接口:1001
2019-08-16 15:55:58.522130+0800 多线程应用[29787:7613734] 开始执行接口:1003
2019-08-16 15:55:58.522168+0800 多线程应用[29787:7613735] 开始执行接口:1002
2019-08-16 15:56:00.602620+0800 多线程应用[29787:7613660] 接口执行完成:1001,耗时:1.90s
2019-08-16 15:56:00.822656+0800 多线程应用[29787:7613660] 接口执行完成:1003,耗时:2.10s
2019-08-16 15:56:00.822934+0800 多线程应用[29787:7613660] 接口执行完成:1002,耗时:2.20s
2019-08-16 15:56:00.823172+0800 多线程应用[29787:7613735] 接口全部执行完毕!
2019-08-16 15:56:00.823591+0800 多线程应用[29787:7613660] refresh UI on Thread: {number = 1, name = main}
*/
Semaphore讲解:
GCD 中的信号量是指 Dispatch Semaphore, 是持有计数的信号。类似于过高速路收费站的栏杆。可以通过时,打开栏杆,不可以通过时,关闭栏杆。在 Dispatch Semaphore 中,使用计数来完成这个功能,计数为0时等待,不可通过。计数为1或大于1时,计数减1且不等待,可通过。
Dispatch Semaphore 提供了三个函数。
1.dispatch_semaphore_create:创建一个Semaphore并初始化信号的总量
2.dispatch_semaphore_signal:发送一个信号,让信号总量加1
3.dispatch_semaphore_wait:可以使总信号量减1,当信号总量为0时就会一直等待(阻塞所在线程), 否则就可以正常执行。
注意:信号量的使用前提是:想清楚你需要处理哪个线程等待(阻塞),又要哪个线程继续执行,然后使用信号量。
链接:https://www.jianshu.com/p/0dc231328ece
来源:
NSOperation讲解:
NSOperation的finished属性被KVO监听, 如果一旦finished, 就执行completionBlock.
NSOperation 属性:
NSOperation *op;
// 判断操作是否正在在运行
op.isExecuting;
// 判断操作是否已经结束
op.isFinished;
// 判断操作是否已经标记为取消
op.isCancelled;
// 判断操作是否处于准备就绪状态,这个值和操作的依赖关系相关
op.isReady;
方法:
(void)waitUntilFinished; // 阻塞当前线程,直到该操作结束。可用于线程执行顺序的同步。
(void)setCompletionBlock:(void (^)(void))block; // completionBlock 会在当前操作执行完毕时执行 completionBlock。
(void)addDependency:(NSOperation *)op; // 添加依赖,使当前操作依赖于操作 op 的完成。
(void)removeDependency:(NSOperation *)op; // 移除依赖,取消当前操作对操作 op 的依赖。
@property (readonly, copy) NSArray
链接:
https://www.jianshu.com/p/4b1d77054b35
https://www.jianshu.com/p/65629ba90d6a
http://www.cocoachina.com/cms/wap.php?action=article&id=24524
自定义NSOperation:
*自定义并发的NSOperation需要以下步骤:
1.start方法:该方法必须实现
2.main:该方法可选,如果你在start方法中定义了你的任务,则这个方法就可以不实现, 但通常为了代码逻辑清晰,通常会在该方法中定义自己的任务
3.isExecuting isFinished 主要作用是在线程状态改变时,产生适当的KVO通知
4.isConcurrent :必须覆盖并返回YES
//MyOperation.h
@interface MyOperation : NSOperation
@property(nonatomic,strong) NSString *methodCode;
@end
//MyOperation.m
#import "MyOperation.h"
typedef void (^resultBlock)(BOOL isOk, NSData *result);
@interface MyOperation()
// 声明属性(父类虽然有, 但是最后重新声明)
@property (assign, nonatomic, getter = isExecuting) BOOL executing;
@property (assign, nonatomic, getter = isFinished) BOOL finished;
@end
@implementation MyOperation
// 手动合成两个实例变量 _executing, _finished, 因为父类设置成ReadOnly
@synthesize executing = _executing;
@synthesize finished = _finished;
- (instancetype)init {
self = [super init];
if (self) {
self.finished = NO;
self.executing = NO;
}
return self;
}
//isConcurrent :必须覆盖并返回YES
- (BOOL)isConcurrent {
return YES;
}
// finished 和 excuting 的 setter 需要通过KVO对外通知.
- (void)setFinished:(BOOL)finished {
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
- (void)setExecuting:(BOOL)executing {
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
- (void)start{
NSLog(@"operation start");
//第一步就要检测是否被取消了,如果取消了,要实现相应的KVO
if ([self isCancelled]) {
self.finished = YES;
return;
}
//如果没被取消,开始执行任务
[NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
self.executing = YES;
}
- (void)main{
NSLog(@"operation main");
@try {
__weak typeof(self) weakSelf = self;
//在这里我们要创建自己的释放池,因为这里我们拿不到主线程的释放池
@autoreleasepool {
[self requestNetDataWithParams:self.methodCode andResultBlock:^(BOOL isOk, NSData *result) {
NSLog(@"request result");
// NSOperation的finished属性被KVO监听, 如果一旦finished, 就执行completionBlock.
weakSelf.finished = YES;
weakSelf.executing = NO;
}];
}
} @catch (NSException *exception) {
NSLog(@"%@",exception);
}
}
-(void)requestNetDataWithParams:(NSString *)params andResultBlock:(resultBlock)resultBlock{
float t = 0.5 + arc4random_uniform(20)/10.0;
NSLog(@"开始执行接口:%@", params);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(t * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSString *message = [NSString stringWithFormat:@"接口执行完成:%@",params];
NSLog(@"%@,耗时:%.2fs", message, t);
NSData *data = [message dataUsingEncoding:NSUTF8StringEncoding];
resultBlock(YES, data);
});
}
@end
-(void)buttonClicked{
MyOperation *op = [[MyOperation alloc] init];
op.methodCode = @"1001";
[op setCompletionBlock:^{
NSLog(@"op Completion");
}];
MyOperation *op2 = [[MyOperation alloc] init];
op2.methodCode = @"1002";
[op2 setCompletionBlock:^{
NSLog(@"op2 Completion");
}];
NSOperationQueue *queue = [NSOperationQueue new];
[queue addOperation:op];
[queue addOperation:op2];
}
/*执行结果:
2019-08-16 16:11:33.108319+0800 多线程应用[29890:7630414] operation start
2019-08-16 16:11:33.108325+0800 多线程应用[29890:7630416] operation start
2019-08-16 16:11:33.108790+0800 多线程应用[29890:7630464] operation main
2019-08-16 16:11:33.108775+0800 多线程应用[29890:7630465] operation main
2019-08-16 16:11:33.109011+0800 多线程应用[29890:7630464] 开始执行接口:1002
2019-08-16 16:11:33.109022+0800 多线程应用[29890:7630465] 开始执行接口:1001
2019-08-16 16:11:34.046483+0800 多线程应用[29890:7630337] 接口执行完成:1002,耗时:0.90s
2019-08-16 16:11:34.046686+0800 多线程应用[29890:7630337] request result
2019-08-16 16:11:34.046944+0800 多线程应用[29890:7630415] op2 Completion
2019-08-16 16:11:35.509324+0800 多线程应用[29890:7630337] 接口执行完成:1001,耗时:2.40s
2019-08-16 16:11:35.509547+0800 多线程应用[29890:7630337] request result
2019-08-16 16:11:35.509882+0800 多线程应用[29890:7630415] op Completion
*/