一、调度组
- 有时候需要在多个异步任务都执行完成之后继续做某些事情,比如下载歌曲,等所有的歌曲都下载完毕之后 转到 主线程提示用户
//1 全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//2 调度组
dispatch_group_t group = dispatch_group_create();
//3 添加任务
//把任务添加到队列,等任务执行完成之后通知调度组
dispatch_group_async(group, queue, ^{
NSLog(@"歌曲1 %@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"歌曲2 %@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"歌曲3 %@",[NSThread currentThread]);
});
//4 所有任务都执行完成后,获得通知 (异步执行)
//等调度组中队列的任务完成后,把block添加到指定的队列
// dispatch_group_notify(group, queue, ^{
// NSLog(@"okkkkkkk %@",[NSThread currentThread]);
// });
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//更新UI控件,提示用户
NSLog(@"okkkkkkk %@",[NSThread currentThread]);
});
NSLog(@"over");
调度组原理
在终端中 输入man dispatch_group_async
//1 全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//2 调度组
dispatch_group_t group = dispatch_group_create();
//ARC中不用写
// dispatch_retain(group);
//3 进入调度组,执行此函数后,再添加的异步执行的block都会被group监听
dispatch_group_enter(group);
//4 添加任务
dispatch_async(queue, ^{
NSLog(@"!!--!!");
dispatch_group_leave(group);
//ARC中此行不用写,也不能写
// dispatch_release(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"!!------!!");
dispatch_group_leave(group);
//ARC中此行不用写,也不能写
//dispatch_release(group);
});
//5 获得调度组的通知
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"okkkkkkk %@",[NSThread currentThread]);
});
//6 等待调度组 监听的队列中的所有任务全部执行完毕,才会执行后续代码,会阻塞线程(很少使用)
//下载三首歌曲,当歌曲都下载完毕 通知用户
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self demo2];
}
//调度组内部的原理
- (void)demo2 {
//创建组
dispatch_group_t group = dispatch_group_create();
//创建队列
dispatch_queue_t queue = dispatch_queue_create("hm", DISPATCH_QUEUE_CONCURRENT);
//任务1
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"任务1");
dispatch_group_leave(group);
});
//任务2
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"任务2");
dispatch_group_leave(group);
});
//任务3
dispatch_group_enter(group);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"任务3");
dispatch_group_leave(group);
});
//等待组中的任务都执行完毕,才会执行
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"over");
});
//等待组中的任务都执行完毕,才会继续执行后续的代码
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"hello ");
}
//演示调度组的基本使用
- (void)demo1 {
//创建组
dispatch_group_t group = dispatch_group_create();
//队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//下载第一首歌曲
dispatch_group_async(group, queue, ^{
NSLog(@"正在下载第一个歌曲");
});
//下载第二首歌曲
dispatch_group_async(group, queue, ^{
NSLog(@"正在下载第二个歌曲");
[NSThread sleepForTimeInterval:2.0];
});
//下载第三首歌曲
dispatch_group_async(group, queue, ^{
NSLog(@"正在下载第三个歌曲");
});
//当三个异步任务都执行完成,才执行
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"over %@",[NSThread currentThread]);
});
}
二、NSOperation
- NSOperation的作用
- 是OC语言中基于GCD的面向对象的封装
- 使用起来比GCD更加简单(面向对象)
- 提供了一些用GCD不好实现的功能
- 苹果推荐使用,使用NSOperation不用关心线程以及线程的生命周期
- 查看NSOperation的头文件
- NSOperation是一个抽象类
- 不能直接使用(方法没有实现)
- 约束子类都具有共同的属性和方法
- NSOperation的子类
- NSInvocationOperation
- NSBlockOperation
- 自定义operation
- NSOperationQueue队列
使用步骤
- NSOperation和NSOperationQueue实现多线程的具体步骤
- 先将需要执行的操作封装到一个NSOperation对象中
- 然后将NSOperation对象添加到NSOperationQueue中
- 系统会自动将NSOperationQueue中的NSOperation取出来
- 将取出的NSOperation封装的操作放到一条新线程中执行
- 建NSInvocationOperation对象
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
- 调用start方法开始执行操作
- (void)start;
一旦执行操作,就会调用target的sel方法
注意
- 默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
- 只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作。
- 创建NSBlockOperation对象
+ (id)blockOperationWithBlock:(void (^)(void))block;
- 通过addExecutionBlock:方法添加更多的操作
- (void)addExecutionBlock:(void (^)(void))block;
注意:只要NSBlockOperation封装的操作数 > 1,就会异步执行操作
三、NSOperationQueue
- NSOperationQueue的作用
NSOperation可以调用start方法来执行任务,但默认是同步执行的
如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
- 添加操作到NSOperationQueue中
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block;
@interface ViewController ()
//全局队列
@property (nonatomic, strong) NSOperationQueue *queue;
@end
@implementation ViewController
//懒加载
- (NSOperationQueue *)queue {
if (_queue == nil) {
_queue = [[NSOperationQueue alloc] init];
}
return _queue;
}
//演示start
- (void)demo1 {
//创建操作
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"hello %@",[NSThread currentThread]);
}];
//更新op的状态,执行main方法
[op start]; //不会开新线程
}
//把操作添加到队列
- (void)demo2 {
//创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//创建操作
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"hello %@",[NSThread currentThread]);
}];
//把操作添加到队列中
[queue addOperation:op];
}
- (void)demo3 {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
NSLog(@"hello %@",[NSThread currentThread]);
}];
}
//全局队列
- (void)demo4 {
// //并发队列,异步执行
// for (int i = 0; i < 10; i++) {
// [self.queue addOperationWithBlock:^{
// NSLog(@"hello %d %@",i,[NSThread currentThread]);
// }];
// }
}
// 操作的 completionBlock
- (void)demo5 {
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:2];
NSLog(@"hello %@",[NSThread currentThread]);
}];
//操作完成之后执行
[op setCompletionBlock:^{
NSLog(@"end %@",[NSThread currentThread]);
}];
[self.queue addOperation:op];
}
四、线程间通信
- 主队列
- 添加到主队列的操作,最终都执行在主线程上
- [NSOperationQueue mainQueue]
- 当前队列
- 获取当前操作所在的队列
- [NSOperationQueue currentQueue]
- 模拟当图片下载完成后回归到主线程上更新UI
@interface ViewController ()
//全局队列
@property (nonatomic, strong) NSOperationQueue *queue;
@end
@implementation ViewController
//懒加载
- (NSOperationQueue *)queue {
if (_queue == nil) {
_queue = [[NSOperationQueue alloc] init];
}
return _queue;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.queue addOperationWithBlock:^{
//异步下载图片
NSLog(@"异步下载图片");
//获取当前队列
// [NSOperationQueue currentQueue]
//线程间通信,回到主线程更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"更新UI");
}];
}];
}
1 消息循环
主线程的消息循环默认开启,子线程的消息循环默认不开启
2 消息循环的目的
保证程序不退出
负责处理输入事件
3 输入事件和消息循环的模式
输入事件的模式必须跟当前消息循环的模式匹配,输入事件才会执行
4 子线程消息循环 run
5 GCD
队列 串行队列 并行队列 全局(并行)队列 主队列
任务
执行 同步执行 异步执行
6 各种组合
6.1 串行队列
同步执行 不开线程,顺序执行
异步执行 开一个线程,顺序执行
6.2 并行队列(全局队列)
同步执行 不开线程,顺序执行
异步执行 开多个线程,无序执行
6.3 主队列
同步执行 在主线程上执行的话会死锁
异步执行 主线程,顺序执行
7 Barrier 阻塞 多线程中操作线程不安全的类,要小心
8 after 延迟执行
9 once 一次性执行 线程安全的
单例 保证一个程序中只有一份单例对象(工具类)
10 调度组 让多个异步任务 都完成之后做一件事情
11 NSOperation 抽象类 抽象类中的方法都没有实现,等待子类去实现(约束子类)
NSInvocationOperation
NSBlockOperation
start 不开线程 会设置操作的状态 会调用main
操作添加到队列 -- 并行队列,异步执行 开多个线程,无序执行
12 NSOperationQueue