• GCD 面试题


    今天我们讲解几道这两天遇到的面试题--GCD编程的.题目很不错,很考究关于GCD的基本概念和使用.

    对于基本的概念,本人博客已在前面讲过,本篇主要以面试题来讲解.大家可看一下本人关于GCD的基本讲解 https://www.cnblogs.com/guohai-stronger/p/9038567.html

    GCD编程的核心就是dispatch队列, dispatch block的执行都会最终放到某个队列中执行.我们直接以题目来讲解:

    【例1】GCD 1

    - (void)viewDidLoad {
        [super viewDidLoad];
        [self testGCD];
    }
    
    - (void)testGCD {
        //并行队列
        dispatch_queue_t queue = dispatch_queue_create("zxy", DISPATCH_QUEUE_CONCURRENT);
        NSLog(@"1");
        dispatch_async(queue, ^{
            NSLog(@"2");
            dispatch_async(queue, ^{
                NSLog(@"3");
            });
            NSLog(@"4");
        });
        NSLog(@"5");
    }

    讲解

    1. 首先看创建队列是并行队列,无需等待;
    2. 首先打印出,然后看dispatch_async,将dispatch_async代码段整个看做事物,对于并行队列,并不会等待,然后绕过dispatch_async事物,执行下面的内容,执行完下面内容后再次执行该事物;
    3. 紧接着打印出 ,执行完打印5,再次会执行dispatch_async整个代码段,看里面的内容有打印出 ,还有一个dispatch_async,还有打印出 4
         NSLog(@"2");
          dispatch_async(queue, ^{
                  NSLog(@"3");
              });
          NSLog(@"4");
    4. 又因为是dispatch_async,以及后面打印出 4 ,又是并行队列,异步执行,所以会先执行打印 4
    5. 执行完打印出 4 后,就会执行 3

    验证结果截图

    【例2】GCD 2

    - (void) testGCD2 {
        dispatch_queue_t queue = dispatch_queue_create("zxy", DISPATCH_QUEUE_CONCURRENT);
        NSLog(@"1");
        dispatch_async(queue, ^{
            NSLog(@"2");
            dispatch_sync(queue, ^{
                NSLog(@"3");
            });
            NSLog(@"4");
        });
        NSLog(@"5");
    }

    结果如下:

    【例3】GCD3

    - (void) testGCD2 {
        dispatch_queue_t queue = dispatch_queue_create("zxy", DISPATCH_QUEUE_SERIAL);
        NSLog(@"1");
        dispatch_async(queue, ^{
            NSLog(@"2");
            dispatch_async(queue, ^{
                NSLog(@"3");
            });
            NSLog(@"4");
        });
        NSLog(@"5");
    }

    结果如下:

    【例4】GCD  4

    - (void)viewDidLoad {
        [super viewDidLoad];
        [self testGCD];
    }
    
    - (void)testGCD {
        //串行队列
        dispatch_queue_t queue = dispatch_queue_create("zxy", DISPATCH_QUEUE_SERIAL);
        NSLog(@"1");
        dispatch_async(queue, ^{
            NSLog(@"2");
            dispatch_sync(queue, ^{
                NSLog(@"3");
            });
            NSLog(@"4");
        });
        NSLog(@"5");
    }

    讲解:

    1. 首先是串行队列,只能按顺序执行
    2. 首先打印出 1 ,然后是dispatch_async会开启线程,里面的内容会慢些执行,执行下面的打印 5 
    3. 打印出 5,然后回到dispatch_async里面,异步执行会开启线程,发现里面有NSLog(@“2”),dispatch_syncy是个同步执行,NSLog(@“4”),然后先打印出 2 
    4. 因为外面dispatch_async,打印出2 之后,开始走 NSLog(@“4”),然后再回到同步里面NSLog(@“3”),然后我们发现dispatch_syncy是个同步执行,也就是不执行完同步里面的NSLog(@“3”),就不可能走NSLog(@“4”),所以说两边都在等着对方,造成了死锁
    5. 在程序中会崩溃

    验证结果截图

    例5】GCD 5

    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //问题:以下代码是在主线程执行的,会不会产生死锁
        NSLog(@"执行任务1--%@", NSThread.currentThread);
        dispatch_queue_t queue = dispatch_get_main_queue();
        dispatch_sync(queue, ^{
            NSLog(@"执行任务2--%@",NSThread.currentThread);
        });
        NSLog(@"执行任务3--%@",NSThread.currentThread);
    }

    讲解:

    1. 首先是viewDidLoad是在主队列,首先打印出 1
    2. 执行任务2也是在主队列下queue = dispatch_get_main_queue,由于队列按照FIFO顺序执行,开始将viewDidLoad加入到队列中,然后将“执行任务2”任务加入到队列中,viewDidLoad包含了执行任务2,执行打印任务2不执行完,就不可能执行完viewDidLoad,然后执行打印任务2必须等待着viewDidLoad执行完之后才能执行,导致了死锁!

    例6】GCD 6

    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //问题:以下代码是在主线程执行的,会不会产生死锁
        NSLog(@"执行任务1--%@", NSThread.currentThread);
        dispatch_queue_t queue = dispatch_get_main_queue();
        dispatch_async(queue, ^{
            NSLog(@"执行任务2--%@",NSThread.currentThread);
        });
        NSLog(@"执行任务3--%@",NSThread.currentThread);
    }

    讲解:

    1. 首先是viewDidLoad是在主队列,首先打印出 1
    2. 由于是dispatch_async在主队列下不会开启新的线程,所以还是在当前线程执行,其中还有一个是dispatch_async不要求立马在当前线程同步执行任务,所以先执行任务3,不会造成死锁

    【例7】GCD 7

    dispatch_queue_t queue = dispatch_queue_create("zxy", DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(queue, ^{
            NSLog(@"哈哈哈 1");
        });
        dispatch_async(queue, ^{
            NSLog(@"哈哈哈 2");
        });
        dispatch_sync(queue, ^{
            NSLog(@"哈哈哈 3");
        });
    //////////////////// NSLog(
    @"哈哈哈 0"); dispatch_async(queue, ^{ NSLog(@"哈哈哈 7"); }); dispatch_async(queue, ^{ NSLog(@"哈哈哈 8"); }); dispatch_async(queue, ^{ NSLog(@"哈哈哈 9"); });

    A 1230789   B 1237890  C 3120798  D 2137890  C 3012789

    上面五种答案,哪一个是对的?

    讲解:

    1. 首先是并行队列 DISPATCH_QUEUE_CONCURRENT,可以并行的执行任务
    2. 在哈哈哈 3中是同步执行,所以此后面的代码在3没有执行完之前是不可能执行的,而前面的哈哈哈 1 和哈哈哈 2都是异步,是可以执行完的,但是当哈哈哈3执行完会紧接着执行 哈哈哈0,所以不可能出现7,8,9在0之前执行完
    3. 从上可以判断出 A  C  E 是可能出现的

    将上面的代码运行到xcode得到如下:E答案

    【例8】GCD面试题8

    我们将DISPATCH_QUEUE_CONCURRENT改为DISPATCH_QUEUE_SERIAL

    因为将并行队列改为串行队列,所有任务按部就班执行,所以结果如下(不加以讲解)

    【例9】GCD面试题9

    - (void) testGCD{
        __block NSInteger a = 0;
        while (a < 100) {
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                a++;
                NSLog(@"%ld======%@", a, [NSThread currentThread]);
            });
        }
        NSLog(@"卧槽无情%ld", a);
    }

    讲解:

    1. 首先不可能打印出0,因为有while循环,while不走完,是不可能走后面的.a = 0, 然后进入while循环, 因为是异步,所以就会开启线程执行a ++ , 可能开出的线程状态不好,所以a可能继续为0, ……
    2. 但是在某个时刻,可能线程64状态返回,执行a++, 所以a = 1 ,……
    3. 所以a的最后值是大于等于100(>=100)

    可能状态还不错,从上面看出结果为100

    【例10】GCD 10

    - (void)viewDidLoad {
        [super viewDidLoad];
        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");
        });
    }
    
    - (void)test{
        NSLog(@"2");
    }

    讲解:

    1. performSelector:withObject:afterDelay:的本质是往Runloop中添加定时器
    2. 子线程默认是没有开启Runloop(dispatch_async开启了子线程)

    【例11】GCD 11

    - (void)viewDidLoad {
        [super viewDidLoad];
        NSLog(@"1");
        [self performSelector:@selector(test) withObject:nil afterDelay:0];
        NSLog(@"3");
    }
    
    - (void)test{
        NSLog(@"2");
    }

    讲解:

    1. performSelector:withObject:afterDelay:的本质是往Runloop中添加定时器
    2. viewDidLoad默认在主线程,主线程默认是开启Runloop,所以执行结果是有打印2

     为什么先执行3,因为执行2在runloop中唤醒的时候执行!

    【例12】GCD 12

    - (void)viewDidLoad {
        [super viewDidLoad];
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        dispatch_async(queue, ^{
            NSLog(@"1");
            [self performSelector:@selector(test) withObject:nil];
            NSLog(@"3");
        });
    }
    
    - (void)test{
        NSLog(@"2");
    }

    讲解:

    • performSelector:withObject:的本质是msgSend,相当于【self test】,和后面加afterDelay完全不一样,Runloop中添加定时器,Runloop有休眠和唤醒状态

    【例13】GCD 13

    - (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");
    }

    讲解:

    • performSelector放在了子线程NSThread,然而当执行完打印1后thread就销毁了,导致了调用performSelector就失败了导致崩溃,如果想要打印出2,可以将线程放在runloop中,
    • 如下
    • - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
          NSThread *thread = [[NSThread alloc]initWithBlock:^{
              NSLog(@"1");
              [[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];
      }
      
      - (void)test{
          NSLog(@"2");
      }

    或者将任务放在了主线程中

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        NSThread *thread = [[NSThread alloc]initWithBlock:^{
            NSLog(@"1");
        }];
        [thread start];
        [self performSelectorOnMainThread:@selector(test) withObject:nil waitUntilDone:YES];
    }
    
    - (void)test{
        NSLog(@"2");
    }

    以上就是GCD的经典面试题,其实看了这几道面试题,发现还都是GCD的基本内容,串行队列,并行队列,以及同步异步是否开启线程的基本概念,希望通过本次讲解,大家对GCD的认识会更上一层,谢谢!!!

  • 相关阅读:
    何为大数据
    开启新征程
    碰撞检测系统
    利用卷积自编码器对图片进行降噪
    Tensorflow实现稀疏自动编码(SAE)
    Linux主机定期打补丁修复漏洞
    值得研究的学问
    TensorFlow 安装教程
    早上收到这样一份通知,求一无漏洞框架,无力吐槽
    如何修改WAMP中mysql默认空密码 以及修改时报错的处理方法
  • 原文地址:https://www.cnblogs.com/guohai-stronger/p/11900622.html
Copyright © 2020-2023  润新知