美文网首页
你好,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

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