美文网首页iOS 的那些事儿iOS功能开发
深入理解 iOS 中的队列和执行

深入理解 iOS 中的队列和执行

作者: uniapp | 来源:发表于2020-04-08 22:42 被阅读0次

GCD 凭借其高性能和易用性,得到了众多开发人员的青睐。在工作中涉及到多线程时,采用的往往也是 GCD。正确使用的前提是理解任务队列这两个概念。所以,iOS 面试中考察基本知识时,往往会涉及到这方面内容。下面就这两个概念,详细介绍。

1 任务

任务是 CPU 进行执行的基本单元,在 iOS 中可以简单地理解为一个函数实现。在GCD中可以认为任务等价于 block。比如,下面的函数体 test 就是一个 task 。

-(void)test {
    NSLog(@"task1");
}
2 队列

队列是任务的集合,强调的是一种静态表示。GCD 中队列分为 2 种: 顺序队列和并发队列。

2.1 顺序队列

如其名,顺序队列里面的任务有先后关系,符合先进先出的基本原则,只有排序在前面的任务执行完成,后面的任务才可以执行。比如下面的两个任务 task1task2 在主线程顺序执行,输出结果如下:

/*
 task1
 task2
 */
- (void)test_queue_1 {
    dispatch_queue_t s1 = dispatch_queue_create("serialOne", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(s1, ^{
        NSLog(@"task1");
    });
    dispatch_sync(s1, ^{
        NSLog(@"task2");
    });
}
2.2 并发队列

并发队列里面的任务,没有先后关系,同时抛出,供线程进行调度。比如,下面在主线程,执行并发队列 c1 中任务 task1、task2。

/*
task1
task2
*/
- (void)test_queue_2 {
    dispatch_queue_t c1 = dispatch_queue_create("concurrentOne", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(c1, ^{
        NSLog(@"task1");
    });
    dispatch_sync(c1, ^{
        NSLog(@"task2");
    });
}
3 执行

执行是对任务的调度,强调动态。执行是通过线程调度来完成的。根据线程调度的方式不同,分为异步和同步。异步是先执行当前线程任务,新添加的任务延迟执行。同步是停止当前线程任务,立即执行新添加的任务。比如,下面在主线程异步执行主队列的任务 task2, 输出结果如下:

/*
task1
task3
task2
*/ 
-(void)test_asyn_1 {
    NSLog(@"task1");
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"task2");
    });
    NSLog(@"task3");
}

关于执行,有以下 3 个特点:

3.1 主队列任务一定在主线程
/*
 task1-<NSThread: 0x600002b72d40>{number = 1, name = main}
 task2-<NSThread: 0x600002b72d40>{number = 1, name = main}
 */
-(void)test_asyn_2 {
    NSLog(@"task1-%@",[NSThread currentThread]);
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"task2-%@",[NSThread currentThread]);
    });
}
3.2 非主队列任务

对顺序队列,同步时不开辟新线程;对顺序队列,异步时开辟新线程,如下所示:

/*
 task1-<NSThread: 0x600003ff20c0>{number = 1, name = main}
 task2-<NSThread: 0x600003ff20c0>{number = 1, name = main}
 task3-<NSThread: 0x600003ff5200>{number = 3, name = (null)}
 */
- (void)test_asyn_3 {
    dispatch_queue_t s1 = dispatch_queue_create("serialOne", DISPATCH_QUEUE_SERIAL);
    NSLog(@"task1-%@",[NSThread currentThread]);
    dispatch_sync(s1, ^{
        NSLog(@"task2-%@",[NSThread currentThread]);
    });
    dispatch_async(s1, ^{
        NSLog(@"task3-%@",[NSThread currentThread]);
    });
}

对并发队列,同步时不开辟新线程;对并发队列,异步时开辟新线程,如下所示:

/*
 task1-<NSThread: 0x60000068ed00>{number = 1, name = main}
 task2-<NSThread: 0x6000006e6240>{number = 6, name = (null)}
 task3-<NSThread: 0x6000006e6240>{number = 6, name = (null)}
 */
- (void)test_asyn_4 {
    dispatch_queue_t s1 = dispatch_queue_create("serialOne", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"task1-%@",[NSThread currentThread]);
    dispatch_async(s1, ^{
        NSLog(@"task2-%@",[NSThread currentThread]);
        dispatch_sync(s1, ^{
            NSLog(@"task3-%@",[NSThread currentThread]);
        });
    });
}

由上:非主队列,是否开辟新线程要看执行者:也就是同步还是异步。同步时,不会开辟新线程;异步时,为了方便任务的及时执行,会开辟新线程;

4 练习

明白了以上概念,下面做两个小练习:

4.1 练习一
-(void)test3_1 {
    NSLog(@"task1-%@",[NSThread currentThread]);
    dispatch_queue_t s1 = dispatch_queue_create("serialOne", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(s1, ^{
        NSLog(@"task2-%@",[NSThread currentThread]);
    });
    NSLog(@"task3-%@",[NSThread currentThread]);
    dispatch_async(s1, ^{
        NSLog(@"task4-%@",[NSThread currentThread]);
    });
    NSLog(@"task5-%@",[NSThread currentThread]);
}

请从打印顺序和是否新开线程,两个角度去思考。思考后,请看
答案:

/*
 task1-<NSThread: 0x600001c42140>{number = 1, name = main}
 task2-<NSThread: 0x600001c42140>{number = 1, name = main}
 task3-<NSThread: 0x600001c42140>{number = 1, name = main}
 task5-<NSThread: 0x600001c42140>{number = 1, name = main}
 task4-<NSThread: 0x600001c2e440>{number = 5, name = (null)}
 */
4.1 练习二
-(void)test3_2 {
    NSLog(@"task1-%@",[NSThread currentThread]);
    dispatch_queue_t s1 = dispatch_queue_create("serialOne", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(s1, ^{
        NSLog(@"task2-%@",[NSThread currentThread]);
    });
    NSLog(@"task3-%@",[NSThread currentThread]);
    dispatch_async(s1, ^{
        NSLog(@"task4-%@",[NSThread currentThread]);
    });
    NSLog(@"task5-%@",[NSThread currentThread]);
}

答案二:

/*
 task1-<NSThread: 0x600001248c00>{number = 1, name = main}
 task2-<NSThread: 0x600001248c00>{number = 1, name = main}
 task3-<NSThread: 0x600001248c00>{number = 1, name = main}
 task5-<NSThread: 0x600001248c00>{number = 1, name = main}
 task4-<NSThread: 0x600001217d00>{number = 3, name = (null)}
 */
4.1 练习三

将 4.2 中的同步队列,改成并发队列,会有什么结果呢?

-(void)test3_3 {
    NSLog(@"task1-%@",[NSThread currentThread]);
    dispatch_queue_t s1 = dispatch_queue_create("serialOne", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(s1, ^{
        NSLog(@"task2-%@",[NSThread currentThread]);
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"task3-%@",[NSThread currentThread]);
        });
    });
    NSLog(@"task4-%@",[NSThread currentThread]);
    dispatch_async(s1, ^{
        NSLog(@"task5-%@",[NSThread currentThread]);
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"task6-%@",[NSThread currentThread]);
        });
    });
    NSLog(@"task7-%@",[NSThread currentThread]);
}

答案三:

/*
 task1-<NSThread: 0x600000145fc0>{number = 1, name = main}
 task2-<NSThread: 0x600000145fc0>{number = 1, name = main}
 task4-<NSThread: 0x600000145fc0>{number = 1, name = main}
 task7-<NSThread: 0x600000145fc0>{number = 1, name = main}
 task5-<NSThread: 0x60000012ea80>{number = 7, name = (null)}
 task3-<NSThread: 0x600000145fc0>{number = 1, name = main}
 task6-<NSThread: 0x600000145fc0>{number = 1, name = main}
 */
4.1 练习四

如果充分理解了以上 3 题,最后一个的题应该就很轻松了:

-(void)test3_4 {
    NSLog(@"task1-%@",[NSThread currentThread]);
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"task2-%@",[NSThread currentThread]);
    });
    NSLog(@"task3-%@",[NSThread currentThread]);
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"task4-%@",[NSThread currentThread]);
    });
    NSLog(@"task5-%@",[NSThread currentThread]);
}

答案四:

/*
 task1-<NSThread: 0x6000027f6d80>{number = 1, name = main}
 task3-<NSThread: 0x6000027f6d80>{number = 1, name = main}
 task5-<NSThread: 0x6000027f6d80>{number = 1, name = main}
 task2-<NSThread: 0x6000027f6d80>{number = 1, name = main}
 task4-<NSThread: 0x6000027f6d80>{number = 1, name = main}
 */

以上是面试过程中遇到的基本知识题,口头表述一般都没问题。遇到像这种做题式的提问,还是需要对其执行有准确的认识,才可以快速得到结果。(完)

喜欢和点赞都是对我的支持和鼓励~

相关文章

  • 深入理解 iOS 中的队列和执行

    GCD 凭借其高性能和易用性,得到了众多开发人员的青睐。在工作中涉及到多线程时,采用的往往也是 GCD。正确使用的...

  • 队列和线程

    [iOS多线程中,队列和执行的排列组合结果分析] 多线程中的队列有:串行队列,并发队列,全局队列,主队列。 执行的...

  • NSTimer

    深入NSTimer(iOS)iOS 中的 NSTimer关于NSRunLoop和NSTimer的深入理解

  • 问题[○○○]:谈谈队列和多线程的使用原理

    在iOS中队列分为以下几种:串行队列:队列中的任务只会顺序执行; 并行队列: 队列中的任务通常会并发执行; 全局队...

  • iOS 队列与线程

    队列和线程是在iOS开发中不可避免的,那么队列与线程有哪些关系呢? 给队列添加任务有四种方式:串行队列中执行同步任...

  • 谈谈队列和多线程的使用原理

    在iOS中队列分为以下几种: 串行队列:队列中的任务只会顺序执行; dispatch_queue_tq = dis...

  • 多线程介绍

    在iOS中队列分为以下几种: 串行队列:队列中的任务只会顺序执行;1dispatch_queue_t q = di...

  • 深入理解iOS中load方法和initialize方

    深入理解iOS中load方法和initialize方法 在ios面试的时候,面试官经...

  • 宏队列与微队列

    js执行时有两个异步队列:宏队列和微队列。优先执行微队列中的任务,而且每次执行完宏队列中的任务后,都会查看微队列中...

  • iOS开发-队列和同步异步执行的结果分析

    多线程中的队列有:串行队列,并发队列,全局队列(并发),主队列(串行)。 执行的方法有:同步执行和异步执行。 提到...

网友评论

    本文标题:深入理解 iOS 中的队列和执行

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