多线程
谈到多线程,一般都会有个疑问:
线程是什么?而谈到线程,不可避免的就是会谈到另外一个词:进程。那么,什么是线程?什么是进程?两者的区别和联系?
- 进程:是系统资源分配和调度的独立单位
- 线程:我的理解就是线程是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
这个任务,只有等这个任务结束了,才能执行其他的。因此双方就都在等待,造成死锁问题。解决方法很简单:既然是双方相互等待造成的问题,那只要让一方不等就行了。这个任务可以执行在其他线程就行了或者不使用同步函数,使用异步函数
-
多线程使用,肯定会有线程安全问题(访问临界资源)
这个就不过多说了,类似的问题很多,比如买票什么的
网友评论