GCD总结

前言

为了缓解主线程的压力除了优化算法,性能等一列措施之外,我们用的最多的应该就是多线程,通过开辟一个分线程,让一些耗时的操作在分线程完成来释放主线程压力,这种方式简单也很高效。但线程并不是越多越好,开辟线程同样需要消耗性能( 开辟新线程:主线程(1M),子线程(512k)),但是实在开发应用中,建议开启线程条数:3~5条最为合理。

GCD实现原理:

GCD有一个底层线程池,这个池中存放的是一个个的线程。之所以称为“池”,很容易理解出这个“池”中的线程是可以重用的,当一段时间后这个线程没有被调用胡话,这个线程就会被销毁。注意:开多少条线程是由底层线程池决定的(线程建议控制再3~5条),池是系统自动来维护,不需要我们程序员来维护,而我们程序员需要关心的是什么呢?我们只关心的是向队列中添加任务,队列调度即可。

1.队列只是负责任务的调度,而不负责任务的执行

2.任务是在线程中执行的

先来说一个队列和任务:

队列分为串行队列与并行队列

任务的执行分为同步任务与异步任务

这两两组合就成为了串行队列同步执行,串行队列异步执行,并行队列同步执行,并行队列异步执行

而异步是多线程的代名词,异步在实际引用中经常会开启新的线程,执行耗时操作,这里为什么说是经常,因为并不是所有的异步任务都会开启一个线程,这个问题后面会具体说道。

队列和任务的特点:

队列的特点 :先进先出,排在前面的任务最先执行,

串行队列:任务按照顺序被调度,前一个任务不执行完毕,队列不会调度

并行队列:只要有空闲的线程,队列就会调度当前任务,交给线程去执行,不需要考虑前面是都有任务在执行,只要有线程可以利用,队列就会调度任务。

主队列:专门用来在主线程调度任务的队列,所以主队列的任务都要在主线程来执行,主队列会随着程序的启动一起创建,我们只需get即可。现将任务放在主队列中,但是不是马上执行,等到主队列中的其它所有除我们使用代码添加到主队列的任务都执行完毕之后才会执行我们使用代码添加的任务。

全局队列:是系统为了方便程序员开发提供的,其工作表现与并发队列一致,那么全局队列跟并发队列的区别是什么呢?

1.全局队列:无论ARC还是MRC都不需要考录释放,因为系统提供的我们只需要get就可以了

2.并发队列:再MRC下,并发队列创建出来后,需要手动释放dispatch_release()

同步执行:不会开启新的线程,任务按顺序执行,当一个任务没有执行完成,不会开始执行新的任务。

异步执行:会开启新的线程,任务可以并发的执行,当一个任务开始执行,下个任务不需要等待前置任务完成即可开始执行。

今天研究的目录

1.串行队列里添加同步任务

2.串行队列添加异步任务

3..并行队列里添加同步任务

4.并行队列添加异步任务

5.全局并发对列

6.主队列

7.并行队列里异步任务嵌套同步任务

8.并行队列里异步任务嵌套异步任务

9.串行队列里异步任务嵌套同步任务

10.串行队列里异步任务嵌套异步任务

11.串行队列里同步任务嵌套同步任务

12.延迟执行

13.定时器

14.group之同步任务

15.group之异步任务

16.apply

17.barrier(栅栏)

18.信号量

接下来一个一个看

1.串行队列里添加同步任务


首先串行队列所有任务依次执行,向串行队列里添加同步任务,同步任务特点,不会开启线程并且当前任务执行完才会执行下一个任务,所以输出顺序为1,2,3。

2.串行队列添加异步任务


从结果可以看到执行顺序还是依次执行,虽然添加的是异步任务,造成这种现象是因为串行队列的特点,就算你是异步任务但还是得按我的规矩来,和上面相比不同的是开辟了一个分线程去执行,因为异步任务具备开启分线程的能力,那么问题来了,为什么三个异步任务只开启一个分线程?这是因为串行队列所有任务顺序执行,开启三个也还是顺序执行,倒不如只开启一个,毕竟开启分线程也是消耗性能的。

3.并行队列里添加同步任务


同步任务的特点,不会开启新的线程并且当前任务执行完才能执行下个任务,那么就算你是并发队列也没什么卵用了,只能依次执行。

4.并行队列添加异步任务


真正能实现并发操作的只有这情况,执行顺序是1,3,2,开启了三个线程来执行任务

5.全局并发对列


系统提供的并发队列,直接获取就行。

6.主队列


主队列不管异步还是同步,都不会开启新的线程,只能在主线程执行。

7.并行队列里异步任务嵌套同步任务


queue是一个并行队列,首先调用dispatch_async函数,将block添加到队列里,由于是异步任务所以会开启一个分线程并且不用等到block的代码执行完就可以继续往下走,所以结束和1谁先执行并不确定,在结束前将主线程休眠2秒,这样1会先走,执行完1接着又调用dispatch_sync函数,将block添加到队列里,由于这是一个同步任务,所以block会立刻执行,接着输出2,当主线程休眠结束会输出结束,结束和2谁先执行也是不确定的,如果主线程休眠时间长,那么2先输出,反之结束先输出,之所以不确定,是因为2在分线程里,所以什么时候会执行无法确定。


8.并行队列里异步任务嵌套异步任务


这个和上面那个并行队列里异步任务嵌套异步任务运行结果是一样的,区别在于2上面在同步任务,这个是在异步任务里,一个马上执行,一个不确定。

9.串行队列里异步任务嵌套同步任务


这个很简答,先走1还是先走结束都是不确定的,但我们再结束前加了一个主线程休眠,那么只会先走1,走完1之后又会向队列里加入一个异步任务,所以2什么时候执行也是不确定的,在这里加了主线程休眠,所以2还是在结束之前执行。

10.串行队列里异步任务嵌套异步任务


串行队列的特点导致就算是多个异步任务也只开启一个线程,因为不管开启几个都是顺序执行,开多了反而浪费资源,就酱。

11.串行队列里同步任务嵌套同步任务


这个组合就有意思,直接崩了。这里需要好好分析一下,首先调用dispatch_sync函数将block添加到queue这个串行队列的最后面,由于是同步任务,所以会立刻执行这个block,然后在这个block里面又调用一个dispatch_sync函数将另一个block添加到queue这个队列的最后面,由于是同步任务,所以会立刻执行这个block,但这是一个串行队列,要想执行后面的blcok需要前面的dispatch_sync函数先返回,但dispatch_sync函数要想返回需要执行完这个block,这就导致了两个任务相互等待,造成死锁。

12.延迟执行

 dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0/*延迟执行时间*/ * NSEC_PER_SEC));
   
            dispatch_after(delayTime, dispatch_get_main_queue(), ^{
                NSLog(@"延迟执行");
            });

13.定时器


14.group之同步任务


通过group可以监听一组任务是否都完成,完成了会自动回调 dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^

        {

            // 完成

            NSLog(@"over");

        });

没个任务都是在分线程中执行的,不占用主线程资源,适用于处理一下复杂的操作,当所有任务结束之后,回调主线程刷新数据。如果里面再添加异步任务,这时候就失效了。

15.group之异步任务

上面说了如果group里面又添加了异步任务,那么上面的机制就不气作用的,如果想要实现同样的功能需要这样做


加一对dispatch_group_enter(group),dispatch_group_leave(group);这样就可以了。

16.apply

如果我们写一个for循环


结束是先执行的,如果我们想要for循环执行完之后再执行结束,那可以这样做


17.barrier(栅栏)


栅栏的就像一个分割线,前面的任务执行完才能执行后面的任务。


18.信号量


信号量在多线程开发中被广泛使用,当一个线程在进入一段关键代码之前,线程必须获取一个信号量,一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待前面的线程释放信号量。

    信号量的具体做法是:当信号计数大于0时,每条进来的线程使计数减1,直到变为0,变为0后其他的线程将进不来,处于等待状态;执行完任务的线程释放信号,使计数加1,如此循环下去。


最后附上一张各种线程锁的效率排行。