iOS中的常见多线程方案
![](https://img.haomeiwen.com/i1345141/e49b0f47b79a7fb5.png)
GCD的常用函数
GCD中有2个用来执行任务的函数
- 用同步的方式执行任务
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
queue
:队列; block
:任务
- 用异步的方式执行任务
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
GCD的队列
GCD的队列可以分为2大类型
-
并发队列(Concurrent Dispatch Queue)
可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
并发功能只有在异步(dispatch_async)函数下才有效 -
串行队列(Serial Dispatch Queue)
让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
容易混淆的术语
有4个术语比较容易混淆:同步、异步、并发、串行
-
同步和异步主要影响:能不能开启新的线程
同步:在当前线程中执行任务,不具备开启新线程的能力
异步:在新的线程中执行任务,具备开启新线程的能力 -
并发和串行主要影响:任务的执行方式
并发:多个任务并发(同时)执行
串行:一个任务执行完毕后,再执行下一个任务
各种队列的执行效果
![](https://img.haomeiwen.com/i1345141/d0e5e5a7fee587cd.png)
使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列(产生死锁)
队列组的使用
思考:如何用gcd实现以下功能
- 异步并发执行任务1、任务2
- 等任务1、任务2都执行完毕后,再回到主线程执行任务3
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//创建队列组
dispatch_group_t group = dispatch_group_create();
//创建并发队列
dispatch_queue_t queue = dispatch_queue_create("queueGroup", DISPATCH_QUEUE_CONCURRENT);
//添加异步函数
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任务一%d == %@",i,[NSThread currentThread]);
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任务二%d == %@",i,[NSThread currentThread]);
}
});
//前两个异步任务执行完成,会自动唤醒,执行当前任务
dispatch_group_notify(group, queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任务三%d == %@",i,[NSThread currentThread]);
}
});
});
//同义代码
//前两个异步任务执行完成,会自动唤醒,执行当前任务
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任务三%d == %@",i,[NSThread currentThread]);
}
});
}
//打印结果
任务二0 == <NSThread: 0x60400027c080>{number = 4, name = (null)}
任务一0 == <NSThread: 0x608000274400>{number = 3, name = (null)}
任务二1 == <NSThread: 0x60400027c080>{number = 4, name = (null)}
任务一1 == <NSThread: 0x608000274400>{number = 3, name = (null)}
任务二2 == <NSThread: 0x60400027c080>{number = 4, name = (null)}
任务一2 == <NSThread: 0x608000274400>{number = 3, name = (null)}
任务二3 == <NSThread: 0x60400027c080>{number = 4, name = (null)}
任务一3 == <NSThread: 0x608000274400>{number = 3, name = (null)}
任务二4 == <NSThread: 0x60400027c080>{number = 4, name = (null)}
任务一4 == <NSThread: 0x608000274400>{number = 3, name = (null)}
任务三0 == <NSThread: 0x604000263a00>{number = 1, name = main}
任务三1 == <NSThread: 0x604000263a00>{number = 1, name = main}
任务三2 == <NSThread: 0x604000263a00>{number = 1, name = main}
任务三3 == <NSThread: 0x604000263a00>{number = 1, name = main}
任务三4 == <NSThread: 0x604000263a00>{number = 1, name = main}
//前两个异步任务执行完成,会自动唤醒,异步执行任务三四
dispatch_group_notify(group, queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任务三%d == %@",i,[NSThread currentThread]);
}
});
});
dispatch_group_notify(group, queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任务四%d == %@",i,[NSThread currentThread]);
}
});
});
面试题
![](https://img.haomeiwen.com/i1345141/4863885421d191bc.png)
- (void)test{
NSLog(@"2");
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1");
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
[self performSelector:@selector(test) withObject:nil afterDelay:.0];
//NSPort可以不添加,因为在NSRunLoop中已经添加了定时器.
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSRunLoopCommonModes];
//只需要执行run就可以了
[[NSRunLoop currentRunLoop] run];
});
NSLog(@"3");
}
打印结果为1,且Crash
- (void)test{
NSLog(@"2");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSThread * thread = [[NSThread alloc] initWithBlock:^{
NSLog(@"1");
}];
[thread start];
[self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];
}
修改代码,但是又产生了新问题
- (void)test{
NSLog(@"2");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSThread * thread = [[NSThread alloc] initWithBlock:^{
NSLog(@"1");
//[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
//RunLoop会执行一次,停止的时候只需要停止当次runLoop即可
// [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
// //RunLoop会一直运行,无法停止;
// [[NSRunLoop currentRunLoop] run];
}];
[thread start];
//上面代码执行完,runloop当中没有source observer timer runloop就会退出
//线程就会销毁.再次使用该线程执行test方法就会Crash
[self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:NO];
//如果传参数为NO,为什么不会crash
}
网友评论