美文网首页
iOS面试题-GCD

iOS面试题-GCD

作者: KG丿夏沫 | 来源:发表于2021-06-07 10:13 被阅读0次

一、在GCD中常见的名次解释:
名词解释

GCD:全称是Grand Central Dispatch,中文名字大脑中枢派发,基于C语言的是苹果公司为解决多核并行问题研发,线程及生命周期都有系统自动管理。

任务:执行什么操作
队列:用来存放任务,将需要执行的任务添加到队列中,队列会遵循FIFO原则(先进先出、后进后出),将队列中的任务取出,放到对应的线程中执行

同步:不创建新的线程,只在当前线程中执行任务
异步:创建多条线程执行任务

串行:同一时间每次只能执行一个任务,当前任务未完成下一个任务只能在队列中等候
并发:同一时间可以执行多个任务

死锁:两个或多个任务互相等待形成死循环阻塞了线程,甚至导致应用无响应

二、关系表

Xnip2021-06-07_10-13-42.png

三、示例代码

    //串行队列
    dispatch_queue_t serial_queue = dispatch_queue_create("custom.serial.queue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(serial_queue, ^{
        NSLog(@"------这是串行队列同步任务----%@-------%@",[NSThread currentThread],[NSThread mainThread]);
    });
    
    dispatch_async(serial_queue, ^{
       NSLog(@"------这是串行队列异步任务----%@-------%@",[NSThread currentThread],[NSThread mainThread]);
    });
    
    //并行队列
    dispatch_queue_t concurrent_queue = dispatch_queue_create("custom.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_sync(concurrent_queue, ^{
        NSLog(@"------这是并行队列同步任务----%@-------%@",[NSThread currentThread],[NSThread mainThread]);
    });
    
    dispatch_async(concurrent_queue, ^{
        NSLog(@"------这是并行队列异步任务----%@-------%@",[NSThread currentThread],[NSThread mainThread]);
    });
    
    //主队列
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"------这是主队列同步任务----%@-------%@",[NSThread currentThread],[NSThread mainThread]);
    });

    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"------这是主队列异步任务----%@-------%@",[NSThread currentThread],[NSThread mainThread]);
    });

1、以上代码运行到主队列同步任务时就会因为线程阻塞导致程序奔溃。当我们暂时注释主队列同步任务,然后运行示例代码,控制台输出如下结果:

------这是串行队列同步任务----<NSThread: 0x600000840f00>{number = 1, name = main}-------<NSThread: 0x600000840f00>{number = 1, name = main}
------这是并行队列同步任务----<NSThread: 0x600000840f00>{number = 1, name = main}-------<NSThread: 0x600000840f00>{number = 1, name = main}
------这是串行队列异步任务----<NSThread: 0x600000805280>{number = 7, name = (null)}-------<NSThread: 0x600000840f00>{number = 1, name = (null)}
------这是并行队列异步任务----<NSThread: 0x60000084ff40>{number = 4, name = (null)}-------<NSThread: 0x600000840f00>{number = 1, name = (null)}
------这是主队列异步任务----<NSThread: 0x600000840f00>{number = 1, name = main}-------<NSThread: 0x600000840f00>{number = 1, name = main}

2、回到刚才的问题,为什么在主队列执行同步任务会出现线程阻塞呢?

因为主线程的任务时按顺序执行,开启同步任务后,主队列在执行到同步任务的时候,开始执行同步任务,但是同步任务又处于等待上一个任务执行完成,线程就会出现阻塞,这就是导致死锁的原因,所以在只用GCD的同步任务需要注意当前队列处于主队列还是其他队列。

四、并发队列

1、名词语解释

并发队列:指在同一时间间隔内执行多个任务,同一时间内只有一个任务在执行

并行队列:指在同一时刻执行多个任务,同一时间内可能有多个任务在执行

2、线程优先级

DISPATCH_QUEUE_PRIORITY_HIGH:         QOS_CLASS_USER_INITIATED   //高优先级
DISPATCH_QUEUE_PRIORITY_DEFAULT:      QOS_CLASS_DEFAULT          //默认优先级
DISPATCH_QUEUE_PRIORITY_LOW:          QOS_CLASS_UTILITY               //最低优先级
DISPATCH_QUEUE_PRIORITY_BACKGROUND:   QOS_CLASS_BACKGROUND  //程序后台执行

3、示例代码

   //并发队列
   dispatch_group_t global = dispatch_group_create();
   
   //低优先任务
   dispatch_queue_global_t low_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
   
   //高优先任务
   dispatch_queue_global_t high_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
   
   //默认
   dispatch_queue_global_t default_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
   
   dispatch_queue_global_t back_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
   
   dispatch_group_async(global, low_queue, ^{
       for (int i = 0; i < 10; i++) {
           NSLog(@"---%@",[NSThread currentThread]);
       }
   });
   
   dispatch_group_async(global, high_queue, ^{
       for (int i = 0; i < 10; i++) {
           NSLog(@"===%@",[NSThread currentThread]);
       }
   });
   
   dispatch_group_async(global, default_queue, ^{
       for (int i = 0; i < 10; i++) {
           NSLog(@"+++%@",[NSThread currentThread]);
       }
   });
   
   dispatch_group_async(global, back_queue, ^{
       for (int i = 0; i < 10; i++) {
           NSLog(@"^^^%@",[NSThread currentThread]);
       }
   });

输出结果:

+++<NSThread: 0x600003752a80>{number = 4, name = (null)}
===<NSThread: 0x600003732a80>{number = 5, name = (null)}
===<NSThread: 0x600003732a80>{number = 5, name = (null)}
+++<NSThread: 0x600003752a80>{number = 4, name = (null)}
+++<NSThread: 0x600003752a80>{number = 4, name = (null)}
===<NSThread: 0x600003732a80>{number = 5, name = (null)}
===<NSThread: 0x600003732a80>{number = 5, name = (null)}
+++<NSThread: 0x600003752a80>{number = 4, name = (null)}
===<NSThread: 0x600003732a80>{number = 5, name = (null)}
+++<NSThread: 0x600003752a80>{number = 4, name = (null)}
===<NSThread: 0x600003732a80>{number = 5, name = (null)}
+++<NSThread: 0x600003752a80>{number = 4, name = (null)}
===<NSThread: 0x600003732a80>{number = 5, name = (null)}
+++<NSThread: 0x600003752a80>{number = 4, name = (null)}
===<NSThread: 0x600003732a80>{number = 5, name = (null)}
+++<NSThread: 0x600003752a80>{number = 4, name = (null)}
===<NSThread: 0x600003732a80>{number = 5, name = (null)}
+++<NSThread: 0x600003752a80>{number = 4, name = (null)}
===<NSThread: 0x600003732a80>{number = 5, name = (null)}
---<NSThread: 0x600003752b00>{number = 3, name = (null)}
^^^<NSThread: 0x600003745740>{number = 6, name = (null)}
+++<NSThread: 0x600003752a80>{number = 4, name = (null)}
---<NSThread: 0x600003752b00>{number = 3, name = (null)}
---<NSThread: 0x600003752b00>{number = 3, name = (null)}
^^^<NSThread: 0x600003745740>{number = 6, name = (null)}
---<NSThread: 0x600003752b00>{number = 3, name = (null)}
---<NSThread: 0x600003752b00>{number = 3, name = (null)}
---<NSThread: 0x600003752b00>{number = 3, name = (null)}
^^^<NSThread: 0x600003745740>{number = 6, name = (null)}
---<NSThread: 0x600003752b00>{number = 3, name = (null)}
---<NSThread: 0x600003752b00>{number = 3, name = (null)}
---<NSThread: 0x600003752b00>{number = 3, name = (null)}
^^^<NSThread: 0x600003745740>{number = 6, name = (null)}
---<NSThread: 0x600003752b00>{number = 3, name = (null)}
^^^<NSThread: 0x600003745740>{number = 6, name = (null)}
^^^<NSThread: 0x600003745740>{number = 6, name = (null)}
^^^<NSThread: 0x600003745740>{number = 6, name = (null)}
^^^<NSThread: 0x600003745740>{number = 6, name = (null)}
^^^<NSThread: 0x600003745740>{number = 6, name = (null)}
^^^<NSThread: 0x600003745740>{number = 6, name = (null)}

从上面输出结果就可以看出并发队列的执行优先级,但是很多时候,我们需要做的任务不是这种,而是并发执行多个任务,等待所有任务完成后,再去执行某个操作,例如:

   dispatch_group_t group = dispatch_group_create();
   
   dispatch_queue_t queue = dispatch_queue_create("com.uploadfile", DISPATCH_QUEUE_CONCURRENT);
   
   NSLog(@"000000");
   
   //子任务一
   dispatch_group_async(group, queue, ^{
       for (int i = 0; i < 5; i++) {
           NSLog(@"111111");
       }
   });
   
   NSLog(@"222222");
   
   //子任务二
   dispatch_group_async(group, queue, ^{
       for (int i = 0; i < 5; i++) {
           NSLog(@"333333");
       }
   });
   
   NSLog(@"444444");
   
   //所有任务完成后
   dispatch_group_notify(group, queue, ^{
       NSLog(@"555555");
   });

控制台输出结果:

2020-04-30 15:44:34.419697+0800 11111111[48973:413079] 000000
2020-04-30 15:44:34.419773+0800 11111111[48973:413079] 222222
2020-04-30 15:44:34.419783+0800 11111111[48973:413241] 111111
2020-04-30 15:44:34.419839+0800 11111111[48973:413079] 444444
2020-04-30 15:44:34.419845+0800 11111111[48973:413241] 111111
2020-04-30 15:44:34.419853+0800 11111111[48973:413240] 333333
2020-04-30 15:44:34.419905+0800 11111111[48973:413241] 111111
2020-04-30 15:44:34.419913+0800 11111111[48973:413240] 333333
2020-04-30 15:44:34.419968+0800 11111111[48973:413241] 111111
2020-04-30 15:44:34.420209+0800 11111111[48973:413240] 333333
2020-04-30 15:44:34.420366+0800 11111111[48973:413241] 111111
2020-04-30 15:44:34.420390+0800 11111111[48973:413240] 333333
2020-04-30 15:44:34.420683+0800 11111111[48973:413240] 333333
2020-04-30 15:44:34.420846+0800 11111111[48973:413240] 555555

分析:其中for循环是为了模拟耗时操作,执行一些耗时任务,可以从打印结果中看出,dispatch_group_notify是等待所有子任务完成后再去执行,比如我们批量上传图片或者文件,然后最后通知用户上传完成,UI更新等操作。

五、面试题

1、GCD是否可以取消某个任务?

答案:是可以的,GCD为我们提供了取消任务的api,接下来继续看下示例

   dispatch_queue_t queue = dispatch_queue_create("com.uploadImage", DISPATCH_QUEUE_CONCURRENT);
   
   dispatch_block_t block1 = dispatch_block_create(0, ^{
       NSLog(@"1");
   });
   
   dispatch_block_t block2 = dispatch_block_create(0, ^{
       NSLog(@"2");
   });
   
   dispatch_block_t block3 = dispatch_block_create(0, ^{
       NSLog(@"3");
   });
   
   dispatch_async(queue, block1);
   dispatch_async(queue, block2);
   dispatch_async(queue, block3);
   dispatch_block_cancel(block1);

以下是输出结果:

2020-04-30 16:14:03.941828+0800 11111111[57038:453729] 2

2020-04-30 16:14:03.941828+0800 11111111[57038:453731] 3

2、如何利用GCD实现想要执行的所有任务执行完成后,再去执行其他任务?

这个知识点就是考虑栅栏函数了,GCD的功能很强大,虽然有一些瑕疵,但是基本能够满足日常需求。

void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);

这个就是栅栏函数,这个函数会阻塞线程,直到在Block块内的代码执行完成后,才能执行后面的代码。

到此基本上常用的GCD相关的面试内容基本完成了,有问题欢迎@我,qq群:960744862

相关文章

网友评论

      本文标题:iOS面试题-GCD

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