美文网首页
你好,GCD

你好,GCD

作者: 顺其自然JX | 来源:发表于2018-04-01 23:31 被阅读472次

1. 并行和并发有什么区别?

并行:同一时刻同时执行不同的任务, 关键点在于同时, 当然想达到这一点至少有两条线程;

并发:同一时间段可以执行不同的任务, 关键在于 可以执行不同的任务, 想做到这一点 一条线程也可以,因为可以在一条线程中交替执行, 多条线程也可以达到, 不同的线程中执行不同的任务;

2. 线程和进程?

进程:具有独立功能的关于某个数据集合上的第一次运行活动,是系统资源分配和调度的独立单位;
线程:是进程的一个实体,是CPU调度和分派的基本代为,它与同一进程中的其他线程共享该进程中所有资源;
区别:
1,一个程序至少一个进程,一个进程至少有一个线程;
2, 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率
3, 线程的划分尺度小于进程,使得多线程程序的并发性高进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了,同一个进程中的多个线程之间可以并发执行

收拾下心情,进入我们的主角GCD:

1、为什么要用GCD?

  • GCD会自动帮我们管理线程的生命周期,它内部维护着一个线程池;
  • 它可以更好的利用多核CPU;
  • 使用起来方便,我们只需要将需要执行的任务放入block就可以了;

2、如何更好的理解任务和队列;

任务:你让程序干的事情,也就是程序需要执行哪些操作;GCD中通常block里面就是一系列的任务;]

任务

队列:队列是用来装任务的,管理任务的,不然任务就乱套了; 它采用FIFO先进先出的原则,不管是串行队列还是并发队列都是一样的,注意这里仅仅指的是出队列的顺序!!!

队列
如图:任务4、3、2、1依次放入队列中,那么任务出去的顺序是4、3、2、1;

串行队列: 队列中的任务只能一个个执行,前面一个没有执行完,后面一个不能执行;

串行队列

并发队列: 前面一个任务出队列开始执行的时候,后面一个就 可以 在新的线程去执行了,注意这里是可以,表示一种能力,不一定会这样;

并发队列
后面一个任务看到前面一个在开始执行状态就可以出队列,准备开始执行了

3、同步和异步

同步:

  • 不开启新的线程就在当前线程执行,
  • 必须等block里面的代码执行完,dispathch才会返回;执行后面的,会阻塞当前线程;

异步:

  • 可以开启新的线程,注意是可以 并不一定会,它有这个能力;
  • 不需要等block里面的代码执行完,立即返回,不会阻塞当前线程;

4、一个关于GCD的故事(本故事纯属本人虚构,如果有不恰当的地方待未来改正)

有两个咖啡店,咖啡店1只有一个窗口卖, 咖啡店2有很多窗口卖, 然后两个老师都带着 排着一条长队的 同学去买咖啡, 其中一个老师A告诉她的学生,你们等前面一个买好再接着买,另一个老师B告诉她的学生,你前面的同学开始买选咖啡的时候,下一个就可以去找其他窗口开始买了,不要等的;

  • A老师把她的同学带到了咖啡店1:
    学生们是只能等前面买好才能买的,而且就只有一个窗口, 所以结果:一个接一个按顺序买就好
  • A老师把她的同学带到了咖啡店2:
    前面一个同学正在买的时候,后面一个想去找其他窗口开始买,但是现实告诉他没有其他窗口了,老老实实等前面一个买好吧,,,,,所以结果:一个接一个按顺序买就好

  • B老师把她的同学带到了咖啡店1:
    虽然现实很好,有好多窗口卖咖啡,但是 学生都是乖孩子,有纪律的,前面一个买好后面才能买,所以就只需要去其他窗口中的一个窗口买就好了,结果:一个接一个按顺序买就好

  • B老师把她的同学带到了咖啡店2:
    这里的孩纸是最幸运的,天时地利人和; 学生可以找其他窗口买,而且这里恰好就有好多窗口,他们几乎同时的在不同的窗户买起咖啡来; 结果:同时买;

5、GCD中的六种组合方式

一. 串行队列 + 同步执行

- (void) GCDTest1
{
    
    dispatch_queue_t queue = dispatch_queue_create("serialQueue.xj.com", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue, ^{
        
        NSLog(@"1------>>>%@",[NSThread currentThread]);
       
    });
    
    dispatch_sync(queue, ^{
        
        NSLog(@"2------>>>%@",[NSThread currentThread]);
        
    });
    
    dispatch_sync(queue, ^{
        
        NSLog(@"3------>>>%@",[NSThread currentThread]);
       
    });
    
        NSLog(@"4------>>>%@",[NSThread currentThread]);
    
}

结果:

2018-04-01 10:16:17.186574+0800 GCD[55764:12462142] 1------>>><NSThread: 0x604000261c00>{number = 1, name = main}
2018-04-01 10:16:17.186850+0800 GCD[55764:12462142] 2------>>><NSThread: 0x604000261c00>{number = 1, name = main}
2018-04-01 10:16:17.187063+0800 GCD[55764:12462142] 3------>>><NSThread: 0x604000261c00>{number = 1, name = main}
2018-04-01 10:16:17.187228+0800 GCD[55764:12462142] 4------>>><NSThread: 0x604000261c00>{number = 1, name = main}

分析:
这里对应上面故事的第一种情况,由于是同步,不开线程,当前线程为主线程,所以所有任务都在主线程中执行,又因为是串行队列,任务按顺序依次执行,而且还是同步,每次需等block内执行完,所以顺序是1-->2--->3--->4;

二. 串行队列 + 异步执行

- (void) GCDTest2
{
    
    dispatch_queue_t queue = dispatch_queue_create("serialQueue.xj.com", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        
        NSLog(@"1------>>>%@",[NSThread currentThread]);
        
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"2------>>>%@",[NSThread currentThread]);
        
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"3------>>>%@",[NSThread currentThread]);
        
    });
    
    NSLog(@"4------>>>%@",[NSThread currentThread]);
    
}

结果:

2018-04-01 10:22:41.780449+0800 GCD[56160:12471344] 4------>>><NSThread: 0x604000079940>{number = 1, name = main}
2018-04-01 10:22:41.780478+0800 GCD[56160:12471596] 1------>>><NSThread: 0x60400027f0c0>{number = 3, name = (null)}
2018-04-01 10:22:41.780803+0800 GCD[56160:12471596] 2------>>><NSThread: 0x60400027f0c0>{number = 3, name = (null)}
2018-04-01 10:22:41.781027+0800 GCD[56160:12471596] 3------>>><NSThread: 0x60400027f0c0>{number = 3, name = (null)}

分析:
因为是异步执行,不会阻塞当前线程,所以先执行4,,开辟了一条新的线程执行1,2,3,然而1,2,3是放到串行队列中的,所以依次然顺序执行1-->2-->3; 这里提一下,既然是异步,为什么不开辟多条线程分别执行1,2,3,没这个必要,既然已经开了一天线程,后面的任务要等前面的执行完再执行,这个线程足以让1,2,3执行任务了,开辟线程是消耗资源的;

三. 并发队列 + 同步执行

- (void) GCDTest3
{
   
   dispatch_queue_t queue =  dispatch_queue_create("xj", DISPATCH_QUEUE_CONCURRENT);;
   
   dispatch_sync(queue, ^{
       
       NSLog(@"1------>>>%@",[NSThread currentThread]);
       
   });
   
   dispatch_sync(queue, ^{
       
       NSLog(@"2------>>>%@",[NSThread currentThread]);
       
   });
   
   dispatch_sync(queue, ^{
       
       NSLog(@"3------>>>%@",[NSThread currentThread]);
       
   });
   
   NSLog(@"4------>>>%@",[NSThread currentThread]);
}

结果

2018-04-01 10:30:24.402362+0800 GCD[56597:12479909] 1------>>><NSThread: 0x60400006f580>{number = 1, name = main}
2018-04-01 10:30:24.432775+0800 GCD[56597:12479909] 2------>>><NSThread: 0x60400006f580>{number = 1, name = main}
2018-04-01 10:30:24.439568+0800 GCD[56597:12479909] 3------>>><NSThread: 0x60400006f580>{number = 1, name = main}
2018-04-01 10:30:24.439568+0800 GCD[56160:12471344] 4------>>><NSThread: 0x604000079940>{number = 1, name = main}

分析:由于是同步执行,所以不会另开线程,阻塞当前线程,所以4是最后执行的,虽然是并发队列可以不用等前面的任务执行完,但是就一条线程,不得不等前面的任务执行完才执行后面的任务,所以1,2,3依次执行;

四. 并发队列 + 异步执行

 - (void) GCDTest4
{
    
    dispatch_queue_t queue =  dispatch_queue_create("xj", DISPATCH_QUEUE_CONCURRENT);;
    
    dispatch_async(queue, ^{
        
        NSLog(@"1------>>>%@",[NSThread currentThread]);
        
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"2------>>>%@",[NSThread currentThread]);
        
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"3------>>>%@",[NSThread currentThread]);
        
    });
    
    NSLog(@"4------>>>%@",[NSThread currentThread]);
    
}

结果:

2018-04-01 10:31:14.552817+0800 GCD[56662:12481282] 4------>>><NSThread: 0x604000069640>{number = 1, name = main}
2018-04-01 10:31:14.552818+0800 GCD[56662:12481369] 2------>>><NSThread: 0x6000002742c0>{number = 4, name = (null)}
2018-04-01 10:31:14.552820+0800 GCD[56662:12481372] 3------>>><NSThread: 0x600000274240>{number = 5, name = (null)}
2018-04-01 10:31:14.552838+0800 GCD[56662:12481370] 1------>>><NSThread: 0x604000268a40>{number = 3, name = (null)}

分析:
这是最常用的一种方式,因为它是异步队列,所以它是不会堵塞当前线程的,4会先执行,由于是并发队列,前面的任务开始执行了,后面的任务便可以出队列,异步执行导致为它单独开辟线程执行,所以1,2,3会在不同的线程中执行,如果是1,2,3分别是耗时的操作,那么它们的执行顺序将不得而知,随机;

五. 主队列 + 同步执行

  • 当前线程为主线程:

    - (void)viewDidLoad {
     [super viewDidLoad];
     
      NSLog(@"1------>>>%@",[NSThread currentThread]);
     
     dispatch_sync(dispatch_get_main_queue(), ^{
         
         
         NSLog(@"2------>>>%@",[NSThread currentThread]);
         
         
     });
     
     NSLog(@"3------>>>%@",[NSThread currentThread]);
    

    结果:打印了1---->>>main后崩溃;

    分析:
    很多博客中把这个案例都说的很好,这里我讲下我的理解,首先viewDidLoad本身就是主线程中执行的,被放在了主队列中,当执行它的时候发现,里面有个同步,必须等block里面的任务执行完才行,也就是必须执行2,但是这时候,2又被放入了主队列,主队列是串行的,很显然,在主队列中2是排在viewDidload任务后面的,那么它必须等viewDidload执行完才可以去执行,然后viewDidload必须等2执行完,才算完,,,,这样陷入死循环,就崩溃了;

  • 当前线程为分线程:
- (void) GCDTest5
{
   NSLog(@"1------>>>%@",[NSThread currentThread]);
   
   dispatch_sync(dispatch_get_main_queue(), ^{
       
       
       NSLog(@"2------>>>%@",[NSThread currentThread]);
       
       
   });
   
   NSLog(@"3------>>>%@",[NSThread currentThread]);
   }

结果:

2018-04-01 10:41:49.864270+0800 GCD[57307:12495333] 1------>>><NSThread: 0x604000664d80>{number = 3, name = (null)}
2018-04-01 10:41:49.868650+0800 GCD[57307:12495215] 2------>>><NSThread: 0x604000261d00>{number = 1, name = main}
2018-04-01 10:41:49.869738+0800 GCD[57307:12495333] 3------>>><NSThread: 0x604000664d80>{number = 3, name = (null)}

分析:
这里不同于上面的点在于,GCDTest5被放在了分线程,那么就好办了,分线程中执行GCDTest5任务时遇到同步,就会一直等,2被放到了主队列,那么2会在所有它前面的主队列中的任务执行完后执行,执行完后分线程中GCDTest5会继续往后执行;

六. 主队列 + 异步执行

- (void) GCDTest6
{
    NSLog(@"1------>>>%@",[NSThread currentThread]);
    
    dispatch_async(dispatch_get_main_queue(), ^{
        
        
        NSLog(@"2------>>>%@",[NSThread currentThread]);
        
        
    });
    
    NSLog(@"3------>>>%@",[NSThread currentThread]);
    
}

结果:

2018-04-01 10:46:06.757861+0800 GCD[57573:12500988] 1------>>><NSThread: 0x60000007f500>{number = 1, name = main}
2018-04-01 10:46:06.758154+0800 GCD[57573:12500988] 3------>>><NSThread: 0x60000007f500>{number = 1, name = main}
2018-04-01 10:46:06.764103+0800 GCD[57573:12500988] 2------>>><NSThread: 0x60000007f500>{number = 1, name = main}

分析:

这个没什么悬念,主队列中的任务是在主线程中执行的,而且主队列是串行队列;

6、线程中的通信方式

7、使用GCD需要注意的地方

8、GCD中的其他重要函数

  • 队列组
    当有多个异步任务的时候,但是我们需要这些任务都完成的时候,才执行一些操作,由于是异步,我们无法保证所有任务的结果,这时候就可以用到dispatch_group, 比如说我们一个首页界面有好多请求,但是我想等所有请求都有结果的时候再显示,不然显示不全会很难看,这时候我们可以这样:
dispatch_group_t group = dispatch_group_create();
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    
    dispatch_group_async(group, queue, ^{
        
        for (int i = 0; i < 3; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
        
    });
   
    dispatch_group_async(group, queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
        
    });
   
    dispatch_group_notify(group, queue, ^{
        
        NSLog(@"---所有任务完成了--->>");
        
        
    });

相关文章

  • 你好,GCD

    1. 并行和并发有什么区别? 并行:同一时刻同时执行不同的任务, 关键点在于同时, 当然想达到这一点至少有两条...

  • 多线程之GCD

    GCD介绍 1、GCD简介 2、GCD任务和队列 3、GCD 的基本使用 4、GCD 线程间的通信 5、GCD 的...

  • 扩展GCD(求逆元,解同余方程等等)

    首先要知道gcd函数的基本性质:gcd(a,b)=gcd(b,a)=gcd(|a|,|b|)=gcd(b,a%b)...

  • iOS - GCD

    目录 GCD简介 GCD核心概念 GCD队列的使用 GCD的常见面试题 GCD简介 Grand Central D...

  • iOS-多线程:GCD

    GCD 简介 GCD 任务和队列 GCD 的使用步骤 GCD 的基本使用(6种不同组合区别) GCD 线程间的通信...

  • 浅析GCD

    GCD目录: 1. GCD简介 为什么要用GCD呢? GCD可用于多核的并行运算GCD会自动利用更多的CPU内核(...

  • 7.3 多线程-GCD

    多线程-GCD 多线程-GCD-串行并行 多线程-GCD.png GCD-线程的通讯、延时操作、定时器 GCD-线...

  • iOS 多线程--GCD

    一、GCD基本介绍 1.GCD简介 GCD是Grand Central Dispatch的缩写,GCD是苹果推出的...

  • 自用算法模板(JAVA版)

    一、数论 1)GCD GCD(求最大公约数) QGCD(快速GCD) extGCD(拓展GCD,解决ax + by...

  • GCD介绍

    一、GCD简单介绍 什么是GCD GCD优势 任务和队列 GCD有2个核心概念 GCD的使用就2个步骤 将任务添加...

网友评论

      本文标题:你好,GCD

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