多线程

作者: 曹来东 | 来源:发表于2018-09-10 16:56 被阅读4次

    iOS中的常见多线程方案

    image.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个术语比较容易混淆:同步、异步、并发、串行

    • 同步和异步主要影响:能不能开启新的线程
      同步:在当前线程中执行任务,不具备开启新线程的能力
      异步:在新的线程中执行任务,具备开启新线程的能力

    • 并发和串行主要影响:任务的执行方式
      并发:多个任务并发(同时)执行
      串行:一个任务执行完毕后,再执行下一个任务

    各种队列的执行效果

    image.png

    使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列(产生死锁)

    队列组的使用

    思考:如何用gcd实现以下功能

    1. 异步并发执行任务1、任务2
    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]);
                }
            });
            
        });
    

    面试题

    image.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
    }
    

    相关文章

      网友评论

          本文标题:多线程

          本文链接:https://www.haomeiwen.com/subject/wdssgftx.html