iOS 多线程【GCD】知识详解

多线程


谈到多线程,一般都会有个疑问:

线程是什么?而谈到线程,不可避免的就是会谈到另外一个词:进程。那么,什么是线程?什么是进程?两者的区别和联系?

  • 进程:是系统资源分配和调度的独立单位
  • 线程:我的理解就是线程是CPU调度和分派的基本单位

举个例子:现在有一个工厂,这个工厂有三个车间:车间A,B和C,每个车间都有4台机器。A负责生产鞋带,B负责生产鞋底,C负责生产鞋身。现在我们收到了一批订单,要生产1000双鞋。那开始生产的时候,就是车间为一个单位,获取资源开始工作,每个车间里,又分派给4台机器开始去工作进行生产。
在这里,车间就类似我们的进程。车间里的线程就是我们的线程。

因此,我们可以知道,一个进程里至少得有一条线程才可以工作。

使用多线程的好处是:可以充分利用CPU的资源进行并发工作


现在来看看iOS里面的多线程

iOS里面使用多线程主要是两种途径GCD和NSOpeartionQueue

GCD 是苹果对C语言线程的封装,使用还是利用c语言的函数来,据说特点是能够高效的利用设备的性能

NSOpeartionQueue 是苹果对GCD的封装,封装成大家熟悉的对象,直接用对象语言去操作

这里我们主要是介绍GCD

要了解GCD,首先需要了解的是四个概念:同步函数,异步函数,并发队列和串行队列

  • 同步函数 dispatch_sync 开头的函数。特点是需要这个函数的代码执行完毕,后面的代码才可以进行

  • 异步函数 dispatch_async 开头的函数。特点是后面的代码不需要等待这个函数的代码执行完毕,可以同时进行

  • 并发队列 队列里的任务可以并发执行

    并发队列的获取方式有两种:
    1.通过dispatch_queue_create函数创建

     dispatch_queue_t   queue = dispatch_queue_create("first_concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
    

    2.通过dispatch_get_global_queue函数可以获取系统提供的全局并发队列

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0)

  • 串行队列 队列里的任务是一个接一个执行的

串行队列的获取方式也有两种:1.通过dispatch_queue_create函数创建。 2.通过dispatch_get_main_queue函数可以获取系统提供的主队列

1.通过dispatch_queue_create函数创建
dispatch_queue_t queue = dispatch_queue_create("first_serial_queue", DISPATCH_QUEUE_DISPATCH_QUEUE_SERIAL);

2.通过dispatch_get_main_queue()函数可以获取系统提供的主队列
dispatch_queue_t queue = dispatch_get_main_queue()

这个主队列比较重要,也比较特殊。主队列里,只有一条主线程,一般情况下,在整个APP中,大部分的操作都是在这条线程上。UI控件的更新和行为都要在这个线程上执行。因此,它也叫UI线程。

在GCD里,主要是利用这四个进行组合使用,下面我们一个一个来看

  • 同步并发队列

    同步并发

    首先touchesBegan方法是在主线程执行的。从输出可以看到,同步并发队列并不会创建新的子线程,四个个任务都是顺序执行的。

  • 同步串行队列

    同步串行

    从输出看,同步串行队列也不会创建新的子线程来执行任务,并且四个任务也是顺序执行的

  • 异步并发队列

    异步并发

    从输出看,异步并发队列创建了三条线程。因为是异步执行的缘故,输出语句按照逻辑上应该是无序的。个人认为之所以end这条语句先输出,是因为异步操作开辟线程耗费了一点点时间。如果三个任务里执行的是耗时操作,那输出语句就是看哪个任务先执行完成。

  • 异步串行队列

    异步串行

    从输出结果看,异步串行,也创建了一条子线程。三个任务都是在这一条子线程上执行。逻辑上三个任务是有序的。

总结

队列 同步 异步
并发队列 不会创建子线程,顺序执行 会创建多条子线程,任务之间是并发执行
串行队列 不会创建子线程,顺序执行 只会创建一条子线程,子线程上的任务顺序执行
主队列 不会创建子线程,顺序执行 不会创建子线程,任务在主线程上顺序执行

其他函数

dispatch_group 参考来源

是可以将多个任务放在一个任务组里,进行统一的管理

  • dispatch_group_async(group, queue, block)将block任务添加到queue队列,并被group组管理
  • dispatch_group_enter(group)声明dispatch_group_enter(group)下面的任务由group组管理,group组的任务数+1
  • dispatch_group_leave(group)相应的任务执行完成,group组的任务数-1
  • dispatch_group_create()创建一个group组
  • dispatch_group_wait(group1, DISPATCH_TIME_FOREVER)
    当前线程暂停,等待dispatch_group_wait(group1, DISPATCH_TIME_FOREVER)上面的任务执行完成后,线程才继续执行
  • dispatch_group_notify(group1, queue1,block)
    监听group组中任务的完成状态,当所有的任务都执行完成后,触发block块,执行总结性处理。

dispatch_group的使用

dispatch_group_async(group,queue,block)dispatch_group_notify(group,queue,block)组合使用。

同步任务和异步任务

同步任务

输出语句hahahaha会等上面的所有任务都完成了,才会去执行。

异步任务

可以看到,因为里面的任务都是异步执行的,这个时候hahahaha不会等待上面所有的任务完成。

dispatch_group_enter(group) dispatch_group_leave(group)dispatch_group_notify(group1, queue1,block) 组合使用

dispatch_group_enter(group)
dispatch_async(queue, ^{
            sleep(3);
            NSLog(@"====second=====%@",[NSThread currentThread]);
        });
dispatch_group_leave(group)

dispatch_group_async(group, queue, ^{
        dispatch_async(queue, ^{
            sleep(3);
            NSLog(@"====second=====%@",[NSThread currentThread]);
        });
    });

作用是一样的。两种写法而已。


栅栏函数 dispatch_barrier_

作用是可以等这个队列里的任务都完成的时候做一个操作。


栅栏异步

栅栏同步

注意:栅栏函数使用的queue,必须是并行队列,并且不能是系统提供的并发队列

dispatch_apply 快速迭代

快速迭代

利用多线程进行的快速循环,循环次数多的情况下,更加快速。

dispatch_sem 信号量

信号量就是一个资源计数器,对信号量有两个操作来达到互斥,分别是P和V操作。 一般情况是这样进行临界访问或互斥访问的: 设信号量值为1, 当一个进程1运行是,使用资源,进行P操作,即对信号量值减1,也就是资源数少了1个。这是信号量值为0。系统中规定当信号量值为0是,必须等待,知道信号量值不为零才能继续操作。 这时如果进程2想要运行,那么也必须进行P操作,但是此时信号量为0,所以无法减1,即不能P操作,也就阻塞。这样就到到了进程1排他访问。 当进程1运行结束后,释放资源,进行V操作。资源数重新加1,这是信号量的值变为1. 这时进程2发现资源数不为0,信号量能进行P操作了,立即执行P操作。信号量值又变为0.次数进程2咱有资源,排他访问资源。 这就是信号量来控制互斥的原理

作者:纸简书生
链接:http://www.jianshu.com/p/04ca5470f212
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

信号量使用

写在最后-多线程不好的地方

  • 使用不好,会造成死锁问题

    最经典的例子:


    死锁问题

    问题分析:首先touchBegan方法肯定是在主线程执行的。然后我们是在方法里面,进行了一个同步的函数任务,因为是同步任务,那么主线程就要等这个任务结束了,才往后面走。但是这个任务是会被主队列加到主线程里执行,然而这个时候主线程里正在执行touchBegan这个任务,只有等这个任务结束了,才能执行其他的。因此双方就都在等待,造成死锁问题。

    解决方法很简单:既然是双方相互等待造成的问题,那只要让一方不等就行了。这个任务可以执行在其他线程就行了或者不使用同步函数,使用异步函数

  • 多线程使用,肯定会有线程安全问题(访问临界资源)

    这个就不过多说了,类似的问题很多,比如买票什么的