问题:项目中用到的多线程技术
使用dispatch_once_t创建单例
使用dispatch_after延迟做事情
使用dispatch_async异步做一些耗时或者不影响整理流程的操作,比如清除缓存操作,异步网络请求
问题:六种组合
|----------------------------------------------------------------------- |
| | 并发队列 | 手动创建的串行队列 | 主队列 |
|----------------------------------------------------------------------- |
|同步(sync) | 没有开启新线程 | 没有开启新线程 | 没有开启新线程 |
| | 串行执行任务 | 串行执行任务 | 串行执行任务 |
|----------------------------------------------------------------------- |
|异步(async)| 有开启新线程 | 有开启新线程 | "没有开启"新线程 |
| | 并发执行任务 | 串行执行任务 | 串行执行任务 |
|-----------------------------------------------------------------------
问题:NSOperation
NSOperation的作用
配合使用NSOperation和NSOperationQueue也能实现多线程编程
NSOperation和NSOperationQueue实现多线程的具体步骤
先将需要执行的操作封装到一个NSOperation对象中
然后将NSOperation对象添加到NSOperationQueue中
系统会自动将NSOperationQueue中的NSOperation取出来
将取出的NSOperation封装的操作放到一条新线程中执行
NSOperation的子类
使用NSOperation子类的方式有3种
NSInvocationOperation
NSBlockOperation
自定义子类继承NSOperation,实现内部相应的方法
NSInvocationOperation
创建NSInvocationOperation对象
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
调用start方法开始执行操作
- (void)start;
一旦执行操作,就会调用target的sel方法
注意
默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
- (void)viewDidLoad {
[super viewDidLoad];
[self invocationOperation];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)invocationOperation
{
//以下两步只是在主线程执行run
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
[op start];
}
- (void)run
{
NSLog(@"------%@", [NSThread currentThread]);
}
@end
NSBlockOperation
创建NSBlockOperation对象
+ (id)blockOperationWithBlock:(void (^)(void))block;
通过addExecutionBlock:方法添加更多的操作
- (void)addExecutionBlock:(void (^)(void))block;
调用start方法开始执行操作
- (void)start;
eg:
- (void)viewDidLoad {
[super viewDidLoad];
[self blockOperation];
}
- (void)blockOperation
{
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// 在主线程
NSLog(@"下载1------%@", [NSThread currentThread]);
}];
// 添加额外的任务(随机在主线程执行或者随机在子线程执行)
[op addExecutionBlock:^{
NSLog(@"下载2------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下载3------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下载4------%@", [NSThread currentThread]);
}];
[op start];
}
NSOperationQueue
NSOperationQueue的作用
NSOperation可以调用start方法来执行任务,但默认是同步执行的
如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
添加操作到NSOperationQueue中
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block;
最大并发数的相关方法
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
取消队列的所有操作
- (void)cancelAllOperations;
提示:也可以调用NSOperation的- (void)cancel方法取消单个操作
暂停和恢复队列
- (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列。 队列暂停时(当前正在执行任务是不会停止)(将要调度到线程里任务)
(BOOL)isSuspended;(图片列表:加载+刷新)
操作优先级
设置NSOperation在queue中的优先级,可以改变操作的执行优先级
- (NSOperationQueuePriority)queuePriority;
- (void)setQueuePriority:(NSOperationQueuePriority)p;
优先级的取值
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
操作依赖
NSOperation之间可以设置依赖来保证执行顺序
比如一定要让操作A执行完后,才能执行操作B,可以这么写
[operationB addDependency:operationA]; // 操作B依赖于操作A
可以在不同queue的NSOperation之间创建依赖关系
操作监听
可以监听一个操作的执行完毕
- (void (^)(void))completionBlock;
- (void)setCompletionBlock:(void (^)(void))block;
自定义NSOperation
自定义NSOperation的步骤很简单
重写- (void)main方法,在里面实现想执行的任务
重写- (void)main方法的注意点
自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应
针对上面的自定义NSOperation和NSOperationQueue有如下的demo
首先自定义一个NSOperation,即新建一个类继承于NSOperation
.h
#import <Foundation/Foundation.h>
@interface XMGOperation : NSOperation
@end
.m
#import "XMGOperation.h"
@implementation XMGOperation
/**
* 需要执行的任务 XMGOperation自定义的方法放在这里面,重写main
*/
- (void)main
{
for (NSInteger i = 0; i<1000; i++) {
//确保当前线程取消后能立即生效,视情况而定
if (self.isCancelled) return;
NSLog(@"download1 -%zd-- %@", i, [NSThread currentThread]);
}
//确保当前耗时线程取消后能生效,不执行download2,download3
if (self.isCancelled) return;
for (NSInteger i = 0; i<1000; i++) {
NSLog(@"download2 -%zd-- %@", i, [NSThread currentThread]);
}
if (self.isCancelled) return;
for (NSInteger i = 0; i<1000; i++) {
NSLog(@"download3 -%zd-- %@", i, [NSThread currentThread]);
}
if (self.isCancelled) return;
}
@end
演示三种创建operation场景,并添加到NSOperationQueue中
- (void)operationQueue1
{
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 创建操作(任务)
// 创建NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download2) object:nil];
// 创建NSBlockOperation ,这个依旧子线程
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download3 --- %@", [NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
NSLog(@"download4 --- %@", [NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
NSLog(@"download5 --- %@", [NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download6 --- %@", [NSThread currentThread]);
}];
// 创建自定义的operation对象 ,类名为XMGOperation,
XMGOperation *op5 = [[XMGOperation alloc] init];
// 添加任务到队列中
[queue addOperation:op1]; // [op1 start] ,添加到队列中去,自动默认start
[queue addOperation:op2]; // [op2 start]
[queue addOperation:op3]; // [op3 start]
[queue addOperation:op4]; // [op4 start]
[queue addOperation:op5]; // [op5 start]
}
- (void)download1
{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}
- (void)download2
{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}
演示不用创建Blockoperation场景,直接给NSOperationQueue添加block
- (void)operationQueue2
{
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
#warning 这两步可直接简化为给NSOperationQueue添加block
// 创建操作
// NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
// NSLog(@"download1 --- %@", [NSThread currentThread]);
// }];
// 添加操作到队列中
// [queue addOperation:op1];
//上面两句作用 等同于 addOperationWithBlock
#warning 也就是下面这句 addOperationWithBlock
[queue addOperationWithBlock:^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}];
}
演示NSOperationQueue并发数,设置为1直接就成串行了
- (void)opetationQueue3
{
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 设置最大并发操作数
// queue.maxConcurrentOperationCount = 2;
queue.maxConcurrentOperationCount = 1; // 就变成了子线程的串行队列,
// 添加操作
[queue addOperationWithBlock:^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download3 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download4 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download5 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download6 --- %@", [NSThread currentThread]);
}];
}
演示取消和暂停
- (void)viewDidLoad {
[super viewDidLoad];
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 设置最大并发操作数
queue.maxConcurrentOperationCount = 1; // 就变成了串行队列
// 添加操作
[queue addOperationWithBlock:^{
// NSLog(@"download1 --- %@", [NSThread currentThread]);
// [NSThread sleepForTimeInterval:1.0];
for (NSInteger i = 0; i<5000; i++) {
NSLog(@"download1 -%zd-- %@", i, [NSThread currentThread]);
}
}];
[queue addOperationWithBlock:^{
// NSLog(@"download2 --- %@", [NSThread currentThread]);
// [NSThread sleepForTimeInterval:1.0];
for (NSInteger i = 0; i<1000; i++) {
NSLog(@"download2 --- %@", [NSThread currentThread]);
}
}];
[queue addOperationWithBlock:^{
// NSLog(@"download3 --- %@", [NSThread currentThread]);
// [NSThread sleepForTimeInterval:1.0];
for (NSInteger i = 0; i<1000; i++) {
NSLog(@"download3 --- %@", [NSThread currentThread]);
}
}];
//添加自定义的Operation,在里面阻断外界暂停和取消操作
[queue addOperation:[[XMGOperation alloc] init]];
self.queue = queue;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//演示一 线程的暂停和继续
// 如果在线程里做耗时操作,执行到一半,暂停是暂时不会执行的,直到这个耗时线程做完会暂停
// if (self.queue.isSuspended) {
// // 恢复队列,继续执行
// self.queue.suspended = NO;
// } else {
// // 暂停(挂起)队列,暂停执行
// self.queue.suspended = YES;
// }
//演示二 线程的取消
//把线程取消,直接消亡
[self.queue cancelAllOperations];
//eg:NSOperationQueue线程的暂停和取消功能,如果这个线程正在做一个耗时操作(比如遍历),暂停和取消会等到这个线程结束,才会结束queue,所以为了避免这种情况,自定义一个NSOperation(比如自己定义的XMGOperation),在耗时操作里监听取消和暂停状态,就是一个很好地将解决办法
}
演示线程依赖
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download1----%@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download2----%@", [NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download3----%@", [NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"download4----%@", [NSThread currentThread]);
}
}];
NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download5----%@", [NSThread currentThread]);
}];
//op5线程完成以后调用completionBlock
op5.completionBlock = ^{
NSLog(@"op5执行完毕---%@", [NSThread currentThread]);
};
// 设置依赖 (再添加之前设置依赖)
[op3 addDependency:op1];
[op3 addDependency:op2];
[op3 addDependency:op4];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
}
线程通信
NSThread线程通信
// 回到主线程,显示图片
//waitUntilDone为YES意味着当前方法会卡主,直到完成才会往下走
[self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
// [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
// [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
NSOperation的线程通信
// 回到主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
GCD的线程通信
// 回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
GCD的一些函数
GCD的简单使用
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self syncConcurrent];
}
/**
* 同步函数 + 主队列:(任务会卡住)
*/
- (void)syncMain
{
NSLog(@"syncMain ----- begin");
// 1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
NSLog(@"syncMain ----- end");
}
/**
* 异步函数 + 主队列:只在主线程中执行任务 (这个特例说明了异步函数可以开启新线程,但并不是绝对可以开启新线程)
*/
- (void)asyncMain
{
// 1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.将任务加入队列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
}
/**
* 同步函数 + 串行队列:不会开启新的线程,在当前线程执行任务。任务是串行的,执 任务,再执行下一个任务
*/
- (void)syncSerial
{
// 1.创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
}
/**
* 异步函数 + 串行队列:会开启新的线程,但是任务是串行的,执行完一个任务,再执行下一个任务
*/
- (void)asyncSerial
{
// 1.创建串行队列(串行队列只能创建)
// DISPATCH_QUEUE_SERIAL 和 NULL 都会是串行队列
dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
// dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", NULL);
// 2.将任务加入队列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
}
/**
* 同步函数 + 并发队列:不会开启新的线程(在主线程做)
*/
- (void)syncConcurrent
{
// 1.获得全局的并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
NSLog(@"syncConcurrent--------end");
}
/**
* 异步函数 + 并发队列:可以同时开启多条线程
*/
- (void)asyncConcurrent
{
// 1.0创建一个并发队列
// label : 相当于队列的名字 "com.520it.queue"
// DISPATCH_QUEUE_CONCURRENT 队列类型,并发队列
// dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_CONCURRENT);
// 1.1直接获得全局的并发队列
//DISPATCH_QUEUE_PRIORITY_DEFAULT优先级,作用:优先执行顺序
//默认传0
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.将任务加入队列
dispatch_async(queue, ^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"1-----%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"2-----%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"3-----%@", [NSThread currentThread]);
}
});
NSLog(@"asyncConcurrent--------end");
// dispatch_release(queue); 现在GCD的create现在不需要释放了
}
@end
队列组
在使用GCD进行任务操作时,有时会希望若干个任务执行之间有先后执行的依赖关系。
eg:
// 创建队列组
dispatch_group_t group = dispatch_group_create();
// 创建并发队列
dispatch_queue_t queue = dispatch_queue_create("my_queue", DISPATCH_QUEUE_CONCURRENT);
// 添加异步任务
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任务1-%@", [NSThread currentThread]);
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任务2-%@", [NSThread currentThread]);
}
});
//等前面的任务执行完毕后,会自动执行这个任务
dispatch_group_notify(group, queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任务3-%@", [NSThread currentThread]);
}
});
});
/*下面这是大牛讲解时
//等前面的任务执行完毕后,会自动执行这个任务
dispatch_group_notify(group, dispatch_get_main_queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任务3-%@", [NSThread currentThread]);
}
});
});
*/
dispatch_set_target_queue
用法一:指定优先级
?
用法二:控制队列执行阶层
如果在多个SERIAL Dispatch Queue中用dispatch_set_target_queue函数制定目标为某一个SERIAL Dispatch Queue,那么原本应并行执行的多个SERIAL Dispatch Queue,在目标SERIAL Dispatch Queue上只能同时执行一个处理。如下:
//1.创建目标队列
dispatch_queue_t targetQueue = dispatch_queue_create("test.target.queue", DISPATCH_QUEUE_SERIAL);
//2.创建3个串行队列
dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);
//3.将3个串行队列分别添加到目标队列
dispatch_set_target_queue(queue1, targetQueue);
dispatch_set_target_queue(queue2, targetQueue);
dispatch_set_target_queue(queue3, targetQueue);
dispatch_async(queue1, ^{
NSLog(@"1 in");
[NSThread sleepForTimeInterval:3.f];
NSLog(@"1 out");
});
dispatch_async(queue2, ^{
NSLog(@"2 in");
[NSThread sleepForTimeInterval:2.f];
NSLog(@"2 out");
});
dispatch_async(queue3, ^{
NSLog(@"3 in");
[NSThread sleepForTimeInterval:1.f];
NSLog(@"3 out");
});
打印结果:
2019-05-06 15:52:36.025069+0800 Test[4273:498926] 1 in
2019-05-06 15:52:39.029506+0800 Test[4273:498926] 1 out
2019-05-06 15:52:39.029782+0800 Test[4273:498926] 2 in
2019-05-06 15:52:41.034457+0800 Test[4273:498926] 2 out
2019-05-06 15:52:41.034623+0800 Test[4273:498926] 3 in
2019-05-06 15:52:42.037019+0800 Test[4273:498926] 3 out
//如上,如果不设置targetQueue目标队列,那么queue1queue2queue3乱序跑。
dispatch_after
下面代码3秒之后执行,可以是任何线程
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"waited at east three seconds.");
});
dispatch_group_notify
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
NSLog(@"1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"2");
});
dispatch_group_async(group, queue, ^{
NSLog(@"3");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"done");
});
dispatch_group_wait
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
NSLog(@"1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"2");
sleep(1.0);
});
dispatch_group_async(group, queue, ^{
NSLog(@"3");
});
//DISPATCH_TIME_FOREVER类型也是dispatch_time_t型
//注意点,一旦调用dispatch_group_wait函数,该函数就处于调用状态而不返回。即是当前线程已经卡在这儿,不向后执行,必须等待前面任务处理有结果或者自己设定的dispatch_time_t时间结束。
BOOL result = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
if (result == 0) {//返回值为0,那么全部处理执行结束
NSLog(@"done");
}else{//如果时间过,但是前面的任务还没有跑完,会走到这儿
NSLog(@"任务仍在执行");
};
dispatch_barrier_async
//栅栏的队列不能使全局的并发,要是手动创建的并发 (待考证)
dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
//barrier栅栏效果
//官方解释:前面的执行完才到这,这个执行完才执行后面(★待考证)
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----5-----%@", [NSThread currentThread]);
});
dispatch_apply
该函数按指定的次数将指定的block追加到指定的Dispatch Queue中,并等待全部处理结束。推荐在dispatch_async中非同步的执行dispatch_apply函数。
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"----------------主线程开始------------");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async( queue, ^{
NSLog(@"子线程%@",[NSThread currentThread]);
dispatch_apply(3, queue, ^(size_t index) {
NSLog(@"追加第%zu次任务",index);
});
NSLog(@"done");
//可以回到主线程进行通信了
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"刷新界面");
});
});
NSLog(@"----------------主线程继续------------");
}
打印结果:
2019-05-07 10:16:06.001245+0800 Test[2820:134508] ----------------主线程开始------------
2019-05-07 10:16:06.001436+0800 Test[2820:134508] ----------------主线程继续------------
2019-05-07 10:16:06.001476+0800 Test[2820:134543] 子线程<NSThread: 0x600000c20a00>{number = 3, name = (null)}
2019-05-07 10:16:06.001567+0800 Test[2820:134543] 追加第0次任务
2019-05-07 10:16:06.001637+0800 Test[2820:134543] 追加第1次任务
2019-05-07 10:16:06.001700+0800 Test[2820:134543] 追加第2次任务
2019-05-07 10:16:06.001761+0800 Test[2820:134543] done
2019-05-07 10:16:06.009918+0800 Test[2820:134508] 刷新界面
dispatch_suspend和dispatch_resume
线程的挂起和恢复。dispatch_suspend并不会立即暂停正在运行的block,而是在当前block执行完成后,暂停后续的block执行。
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_queue_create("com.test.gcd", DISPATCH_QUEUE_SERIAL);
//提交第一个block,延时2秒打印。
dispatch_async(queue, ^{
sleep(2);
NSLog(@"After 2 seconds...");
});
//提交第二个block,也是延时2秒打印
dispatch_async(queue, ^{
sleep(2);
NSLog(@"After 2 seconds again...");
});
//延时一秒
NSLog(@"sleep 1 second...");
sleep(1);
//挂起队列
NSLog(@"suspend...");
dispatch_suspend(queue);
//延时10秒
NSLog(@"sleep 10 second...");
sleep(10);
//恢复队列
NSLog(@"resume...");
dispatch_resume(queue);
}
问题:GCD和nsopreation什么场景使用哪个好点
GCD主要特点:
1)GCG 是iOS4.0推出的,主要针对多核CPU 做了优化
2)GCD是 C 语言的技术
3)GCD 提供了一些 NSOperation 不具备的功能,比如一次性执行(创建单例),延迟执行,调度组.
NSOperation 特点:
- NSOperation 是 iOS2.0后推出的,iOS4.0之后重写了NSOperation.
- NSOperation 将操作(异步的任务)添加到队列(并发队列),就会执行制定操作的函数.
- NSOperation里可以方便的设置操作:
1⃣️最大并发数
2⃣️队列的暂停/继续
3⃣️取消所有的操作
4⃣️指定操作之间的依赖关系(GCD可以用同步实现)
**使用NSOperation 需要注意几点点: - 注意避免产生循环依赖
- 要先设置依赖关系,然后添加到队列
GCD 和 NSOperation的区别主要表现在以下几方面:
-
GCD是一套 C 语言API,执行和操作简单高效,因此NSOperation底层也通过GCD实现,这是他们之间最本质的区别.因此如果希望自定义任务,建议使用NSOperation;
-
依赖关系,NSOperation可以设置操作之间的依赖(可以跨队列设置),GCD无法设置依赖关系,不过可以通过同步来实现这种效果;
-
KVO(键值对观察),NSOperation容易判断操作当前的状态(是否执行,是否取消等),对此GCD无法通过KVO进行判断;
(指的应该是:NSOperationQueue中使用了KVO,当NSOperation对象状态变化(finished,canceled等)时,观察者可以知道变化,从而做出相应的处理(移除已完成的等等)) -
优先级,NSOperation可以设置自身的优先级,但是优先级高的不一定先执行,GCD只能设置队列的优先级,如果要区分block任务的优先级,需要很复杂的代码才能实现;
-
继承,NSOperation是一个抽象类.实际开发中常用的是它的两个子类:NSInvocationOperation和NSBlockOperation,同样我们可以自定义NSOperation,GCD执行任务可以自由组装,没有继承那么高的代码复用度;
-
效率,直接使用GCD效率确实会更高效,NSOperation会多一点开销,但是通过NSOperation可以获得依赖,优先级,继承,键值对观察这些优势,相对于多的那么一点开销确实很划算,鱼和熊掌不可得兼,取舍在于开发者自己;
7)可以随时取消准备执行的任务(已经在执行的不能取消),GCD没法停止已经加入queue 的 block(虽然也能实现,但是需要很复杂的代码)
基于GCD简单高效,更强的执行能力,操作不太复杂的时候,优先选用GCD;而比较复杂的任务可以自己通过NSOperation实现.
问题:atomic
atomic用于保证属性setter、getter的原子性操作,相当于在setter和getter内部加了线程同步的锁,从而保证了setter和getter内部是线程同步的。
可以参考源码objc4的objc-accessors.mm
首先看设置属性
void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy)
{
bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
bool mutableCopy = (shouldCopy == MUTABLE_COPY);
reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}
点击查看reallySetProperty方法
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
if (offset == 0) {
object_setClass(self, newValue);
return;
}
id oldValue;
id *slot = (id*) ((char*)self + offset);
if (copy) {
newValue = [newValue copyWithZone:nil];
} else if (mutableCopy) {
newValue = [newValue mutableCopyWithZone:nil];
} else {
if (*slot == newValue) return;
newValue = objc_retain(newValue);
}
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else { //如果是atomic,可以看到加了一个spinlock_t自旋锁
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
再看get属性
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
if (offset == 0) {
return object_getClass(self);
}
// Retain release world
id *slot = (id*) ((char*)self + offset);
if (!atomic) return *slot;
// Atomic retain release world
spinlock_t& slotlock = PropertyLocks[slot]; //如果是atomic,可以看到加了一个spinlock_t自旋锁
slotlock.lock();
id value = objc_retain(*slot);
slotlock.unlock();
// for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
return objc_autoreleaseReturnValue(value);
}
它并不能保证"使用属性"的过程是线程安全的。只是保证了set和get方法内部是线程安全的,但是并不能保证属性的过程是线程安全的。
说atomic并不能保证使用属性的过程是线程安全的,
举个例子
1.对于NSArray类型 @property(atomic)NSArray *array我们用atomic修饰,数组的添加和删除并不是线程安全的,这是因为数组比较特殊,我们要分成两部分考虑,一部分是&array也就是这个数组本身,另一部分是他所指向的内存部分。atomic限制的只是&array部分,对于它指向的对象没有任何限制。
eg:
MJPerson *p = [[MJPerson alloc] init];//初始化类,类里面之前生命了一个可变数组dataArray属性
p.dataArray = [NSMutableArray array];//初始化dataArray,这里面p.dataArray是线程安全的,因为调用了set方法
[p.dataArray addObject:@"1"]; //这里面p.dataArray是线程安全的,因为调用了get方法,但是addObject却没有安全管控
[p.dataArray addObject:@"2"];
2、比如如果线程 A 调了 getter,与此同时线程 B 、线程 C 都调了 setter——那最后线程 A get 到的值,3种都有可能:可能是 B、C set 之前原始的值,也可能是 B set 的值,也可能是 C set 的值。同时,最终这个属性的值,可能是 B set 的值,也有可能是 C set 的值。
如下
@property (atomic, assign) NSInteger intA;
- (void)viewDidLoad {
[super viewDidLoad];
//开启一个线程对intA的值+1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0;i < 1000;i ++){
self.intA = self.intA + 1;
}
NSLog(@"首先加一:intA : %ld",(long)self.intA);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"首先加一后:intA : %ld",(long)self.intA);
});
//开启一个线程对intA的值+1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0;i < 1000;i ++){
self.intA = self.intA + 1;
}
NSLog(@"其次加一:intA : %ld",(long)self.intA);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"其次加一后:intA : %ld",(long)self.intA);
});
}
@end
打印:(因为都是异步,打印顺序并不固定)
2021-02-03 17:39:09.929929+0800 OC_Test[6945:206188] 首先加一:intA : 1000
2021-02-03 17:39:09.930341+0800 OC_Test[6945:206184] 其次加一:intA : 2000
2021-02-03 17:39:09.930385+0800 OC_Test[6945:206186] 其次加一后:intA : 2000
2021-02-03 17:39:09.931724+0800 OC_Test[6945:206189] 首先加一后:intA : 0
atomic:原子性
nonatomic:非原子性
声明属性时不写的话默认就是atomic
忙等
自旋锁(Spinlock)是一种忙等待锁,线程反复检查锁变量是否可用,不会挂起,避免了进程上下文的调度开销,适合阻塞很短时间的场合。当然也就不适合单CPU单线程上使用。
问题:dispatch_sync
意味着将指定的block“同步”追加到指定的Dispatch Queue中,在追加block结束之前,dispatch_sync函数会一直等待。可以认为是简单版的dispatch_group_wait
由于sync特性,所以使用sync函数往当前"串行"队列中添加任务,会卡住当前的串行队列(产生死锁)
死锁的原因是:队列引起的循环等待。由于当前串行队列执行到dispatch_sync处等待dispatch_sync的返回,dispatch_sync必须等待block执行完毕后才能返回,由于当前队列是串行队列,先进先出,但是我们通过dispatch_sync新放入的block位于队列后面,现在得不到执行,所以锁住了。
问题:以下代码是在主线程执行的,会不会产生死锁?会!
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"执行任务2");
});
NSLog(@"执行任务3");
原因:
因为把同步的任务2添加到主队列,dispatch_sync要求立马在当前线程同步执行任务,走到这一步想要立即执行。想要执行队列里的任务2,必须要等前面一整个任务执行完。任务3在等任务2,任务2在等任务3。
用图表示为
当前线程 | 主队列
任务1 | viewDidLoad
sync | 任务2
任务3 |
那么把上述的dispatch_sync换成dispatch_async会不会产生死锁?不会!
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"执行任务2");
});
NSLog(@"执行任务3");
原因:
dispatch_async不要求立马在当前线程同步执行任务
//打印结果为 执行任务1 执行任务3 执行任务2
问题:以下代码会不会产生死锁?不会!
-
(void)viewDidLoad {
[super viewDidLoad];
NSLog(@"执行任务1");dispatch_async(dispatch_get_global_queue(0, 0), ^{
//将上面放在主线程会死锁的代码放到子线程里不会死锁
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"执行任务2");
});
});NSLog(@"执行任务3");
}
问题:以下代码是在主线程执行的,会不会产生死锁?会!
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{ // 0
NSLog(@"执行任务2");
dispatch_sync(queue, ^{ // 1
NSLog(@"执行任务3");
});
NSLog(@"执行任务4");
});
NSLog(@"执行任务5");
问题:以下代码是在主线程执行的,会不会产生死锁?会!
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{ // 0
NSLog(@"执行任务2");
dispatch_sync(queue, ^{ // 1
NSLog(@"执行任务3");
});
NSLog(@"执行任务4");
});
NSLog(@"执行任务5");
问题:以下代码是在主线程执行的,会不会产生死锁?不会!虽是同步,但确实并发队列
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2");
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
// Do any additional setup after loading the view.
}
下面代码执行会打印什么
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"1");
[self performSelector:@selector(test) withObject:nil afterDelay:.0];
NSLog(@"3");
//添加这行代码,启动RunLoop程序才能打印2
//[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
});
}
- (void)test
{
NSLog(@"2");
}
答:会打印 1 3,但是2不会打印。因为performSelector:withObject:afterDelay:这句代码的本质是往Runloop中添加定时器,而子线程的Runloop在这里没有启动。
注意:- (id)performSelector:(SEL)aSelector withObject:(id)object;等不需要Runloop。
下面代码执行会打印什么
- (void)test
{
NSLog(@"2");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSThread *thread = [[NSThread alloc] initWithBlock:^{
NSLog(@"1");
//启动RunLoop,保住线程。然后程序正常运行,打印了2
//[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
//[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}];
[thread start];
[self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];
}
答:会打印 1 ,然后程序崩溃。因为执行完1,block结束后线程结束。
问题:信号量
dispatch_semaphore
semaphore叫做”信号量”
信号量的初始值,可以用来控制线程并发访问的最大数量
信号量的初始值为1,代表同时只允许1条线程访问资源,保证线程同步
//定义一个控制最大量的值
int value= 1;
//初始化信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(value);
//执行到这句话时,如果信号量的值<=0,当前线程会进入睡眠等待(直到信号量的值>0);如果信号量的值>0,就减1,继续往下执行。
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
//让信号量的值加1
dispatch_semaphore_signal(semaphore);
信号量的使用场景
加锁
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
for (int i = 0; i < 10000; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//临界区,即待加锁的代码区域
dispatch_semaphore_signal(semaphore);
});
}
在进行多线程任务之前,首先创建一个计数为1的信号量,这样可以保证同一时刻只有一个线程在访问临界区。上面代码,系统会同时开启很多线程想要做遍历里面的事,但是同时只有一个线程能去做临界区的事情。
异步任务,同步返回
- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
__block NSArray *tasks = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
//task赋值,代码有点长,就不贴了
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return tasks;
}
上面是 AFNetworking 的一段代码,我且称之为异步去做,同步返回。这段代码的功能是通过异步的请求取得键路径为 keyPath 的任务数组 tasks,然后同步返回它。
控制线程并发数
在 GCD 中,dispatch_async() 异步操作可以产生新的线程,但是方法本身没办法限制线程的最大并发数,线程的创建和销毁是由 GCD 底层管理的。
了解 NSOperationQueue 的同学肯定知道,通过 maxConcurrentOperationCount 属性可以设置它的最大并发数。那么在GCD中,对应的解决方法就是使用信号量。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
for (int i = 0; i < 1000; ++i) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//多线程代码
dispatch_semaphore_signal(semaphore);
});
}
其实跟加锁代码非常相似,区别在于,在初始化信号量时,将计数赋值为最大并发数。在应用场景上,限制线程并发数是为了性能考虑,而加锁是为了安全而考虑。
信号量和栅栏函数是锁吗
信号量是属于很多种加锁方式的一种,栅栏函数能实现读写锁的功能,所以应该都属于锁。
问题:线程池
线程池是一种线程使用模式。 线程过多会带来调度开销,进而影响缓存局部性和整体性能。 而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。 这避免了在处理短时间任务时创建与销毁线程的代价。
线程池的执行流程如图下:
1612430117402.jpg若线程池大小小于核心线程池大小时
创建线程执行任务
若线程池大小大于等于核心线程池大小时
1.先判断线程池工作队列是否已满
2.若没满就将任务push进队列
3.若已满时,且maximumPoolSize>corePoolSize,将创建新的线程来4.执行任务
反之则交给饱和策略去处理
参数名代表意义
corePoolSize
线程池的基本大小(核心线程池大小)
maximumPool
线程池的最大大小
keepAliveTime
线程池中超过corePoolSize树木的空闲线程的最大存活时间
unit
keepAliveTime参数的时间单位
workQueue
任务阻塞队列
threadFactory
新建线程的工厂
handler
当提交的任务数超过maxmumPoolSize与workQueue之和时,任务会交给RejectedExecutionHandler来处理
上图的解释
我们知道,受限于硬件、内存和性能,我们不可能无限制的创建任意数量的线程,因为每一台机器允许的最大线程是一个有界值。也就是说ThreadPoolExecutor管理的线程数量是有界的。线程池就是用这些有限个数的线程,去执行提交的任务。然而对于多用户、高并发的应用来说,提交的任务数量非常巨大,一定会比允许的最大线程数多很多。为了解决这个问题,必须要引入排队机制,或者是在内存中,或者是在硬盘等容量很大的存储介质中。J.U.C提供的ThreadPoolExecutor只支持任务在内存中排队,通过BlockingQueue暂存还没有来得及执行的任务。
任务的管理是一件比较容易的事,复杂的是线程的管理,这会涉及线程数量、等待/唤醒、同步/锁、线程创建和死亡等问题。ThreadPoolExecutor与线程相关的几个成员变量是:keepAliveTime、allowCoreThreadTimeOut、poolSize、corePoolSize、maximumPoolSize,它们共同负责线程的创建和销毁。
corePoolSize:
线程池的基本大小,即在没有任务需要执行的时候线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程。这里需要注意的是:在刚刚创建ThreadPoolExecutor的时候,线程并不会立即启动,而是要等到有任务提交时才会启动,除非调用了prestartCoreThread/prestartAllCoreThreads事先启动核心线程。再考虑到keepAliveTime和allowCoreThreadTimeOut超时参数的影响,所以没有任务需要执行的时候,线程池的大小不一定是corePoolSize。
maximumPoolSize:
线程池中允许的最大线程数,线程池中的当前线程数目不会超过该值。如果队列中任务已满,并且当前线程个数小于maximumPoolSize,那么会创建新的线程来执行任务。这里值得一提的是largestPoolSize,该变量记录了线程池在整个生命周期中曾经出现的最大线程个数。为什么说是曾经呢?因为线程池创建之后,可以调用setMaximumPoolSize()改变运行的最大线程的数目。
poolSize:
线程池中当前线程的数量,当该值为0的时候,意味着没有任何线程,线程池会终止;同一时刻,poolSize不会超过maximumPoolSize。
https://www.cnblogs.com/frankyou/p/10135212.html
问题:异步执行ABC之后在执行D的正确理解
首先异步执行ABC分两种情况,一种任务是同步的,一种任务是网络请求,发送操作是同步的,但是请求到的结果是异步的。
任务是同步时
第一种GCD group
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^{
NSLog(@"同步任务A");
});
dispatch_group_async(group, queue, ^{
NSLog(@"同步任务B");
});
dispatch_group_async(group, queue, ^{
NSLog(@"同步任务C");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"任务完成执行");
});
第二种 dispatch_barrier_async
dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"任务A");
});
dispatch_async(queue, ^{
NSLog(@"任务B");
});
dispatch_async(queue, ^{
NSLog(@"任务C");
});
dispatch_barrier_async(queue, ^{
NSLog(@"阻塞自定义并发队列");
});
dispatch_async(queue, ^{
NSLog(@"任务D");
});
dispatch_async(queue, ^{
NSLog(@"任务E");
});
第三种 NSOperation
NSBlockOperation *operatioon1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任务A");
}];
NSBlockOperation *operatioon2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任务B");
}];
NSBlockOperation *operatioon3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任务C");
}];
NSBlockOperation *operatioon4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任务D");
}];
[operatioon4 addDependency:operatioon1];
[operatioon4 addDependency:operatioon2];
[operatioon4 addDependency:operatioon3];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operatioon1,operatioon2,operatioon3,operatioon4] waitUntilFinished:YES];
NSLog(@"完成之后的操作");
任务时异步时(比如网络请求异步任务)
第一种 dispatch_group + semaphore
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_group_async(group, queue, ^{
NSLog(@"同步任务A");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"网络异步任务一");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
dispatch_group_async(group, queue, ^{
NSLog(@"同步任务B");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.8f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"网络异步任务二");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
dispatch_group_async(group, queue, ^{
NSLog(@"同步任务C");
});
dispatch_group_async(group, queue, ^{
NSLog(@"同步任务D");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"网络异步任务四");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
dispatch_group_notify(group, queue, ^{
NSLog(@"任务完成执行");
});
第二种 dispatch_group_enter 和 dispatch_group_leave
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"同步任务A");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"网络异步任务一");
dispatch_group_leave(group);
});
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"同步任务B");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.8f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"网络异步任务二");
dispatch_group_leave(group);
});
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"同步任务C");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"同步任务D");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"网络异步任务四");
dispatch_group_leave(group);
});
});
dispatch_group_notify(group, queue, ^{
NSLog(@"任务完成执行");
});
网友评论