(五十五)iOS多线程之GCD
GCD的全称为Grand Central Dispatch,翻译为大中央调度,是Apple开发的一个多线程编程解决方法。
进程和线程的概念:
正在进行中的程序被称为进程,负责程序运行的内存分配,每一个进程都有自己独立的虚拟内存空间。
线程是进程中一个独立的执行路径,即主线程,主线程有1M的栈区,对于耗时的执行路径,可以放在子线程(512K栈区)中执行。
Tip:新建线程会消耗内存空间和CPU事件,线程太多会降低系统的运行性能,多线程是通过CPU时分复用实现的。
Tip:多线程是为了并发执行多项任务,不会提高单个算法本身的执行效率。
iOS中三种多线程技术:
1.NSThread
建立一个线程方便,但是管理多个线程非常困难,但是NSThread的currentThread方法可以跟踪任务所在的线程。
2.GCD Grand Central Dispatch(基于C语言的底层API),使用block来定义任务,推荐使用。
3.NSOperation/NSOperationQueue 使用GCD的一套OC API,提供了一些GCD中不易实现的特性。
Tip:直接使用GCD更好一些。
GCD的核心思想是把操作(block定义任务)放入队列中,队列的特点是FIFO(先进先出),出队时操作被分配到CPU上进行处理。
Tip:队列不是线程,也不表示对应的CPU,例如双核CPU,谁空闲分配给谁,出队时分配到哪个CPU是不需要被关心的。
Tip:GCD的函数都是以dispatch开头的,dispatch的含义是分派、调度。
Tip:NSThread的currentThread得到的是name和num字典,其中num=1表示主线程。
【进程同步和异步的概念】
进程同步:一个操作没有完成则不返回,必须一件一件的做事情,一件事情返回了才能做下一件事情。
进程异步:多个操作交替进行,操作的返回时机不确定。
【串、并行队列】
Tip:同步为sync,异步为async
1.串行队列的异步任务:使用一个子线程依次执行。
应用:串行队列中的异步任务会依次执行,例如先下载图片,然后处理,两个任务有明确的先后顺序,顺序是确定的。
/** * 串行队列 */ - (void)gcdDemo1{ dispatch_queue_t q = dispatch_queue_create("queue<1>", DISPATCH_QUEUE_SERIAL); for (int i = 0; i < 3; i++) { dispatch_async(q, ^{ NSLog(@"%@ i = %d",[NSThread currentThread],i); //跟踪当前线程 }); } }打印串行队列的输出发现,i的值是从0到2分别输出的:
2015-02-16 17:28:11.559 多线程初步[1493:119215] <NSThread: 0x7986cbb0>{number = 2, name = (null)} i = 0 2015-02-16 17:28:11.559 多线程初步[1493:119215] <NSThread: 0x7986cbb0>{number = 2, name = (null)} i = 1 2015-02-16 17:28:11.559 多线程初步[1493:119215] <NSThread: 0x7986cbb0>{number = 2, name = (null)} i = 2
2.并行队列的异步任务:使用多个子线程无序执行,一般任务较少时几个任务就开几个线程,较多时则开部分线程。
应用:一系列的异步任务没有先后顺序。
/** * 并行队列 */ - (void)gcdDemo2{ dispatch_queue_t q = dispatch_queue_create("queue<2>", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 3; i++) { dispatch_async(q, ^{ NSLog(@"%@ i = %d",[NSThread currentThread],i); //跟踪当前线程 }); } }打印输出,发现i的值是随机排列的:
2015-02-16 17:28:55.084 多线程初步[1521:119972] <NSThread: 0x796347d0>{number = 2, name = (null)} i = 2 2015-02-16 17:28:55.084 多线程初步[1521:119975] <NSThread: 0x79956380>{number = 3, name = (null)} i = 0 2015-02-16 17:28:55.084 多线程初步[1521:119973] <NSThread: 0x796a2ba0>{number = 4, name = (null)} i = 1
3.串行队列的同步任务:只使用主线程顺序执行,用处较少。
/** * 串行队列的同步任务 */ - (void)gcdDemo12{ dispatch_queue_t q = dispatch_queue_create("queue<1>", DISPATCH_QUEUE_SERIAL); for (int i = 0; i < 3; i++) { dispatch_sync(q, ^{ NSLog(@"%@ i = %d",[NSThread currentThread],i); //跟踪当前线程 }); } }
2015-02-16 17:33:12.938 多线程初步[1574:121669] <NSThread: 0x7a25f5a0>{number = 1, name = main} i = 0 2015-02-16 17:33:12.938 多线程初步[1574:121669] <NSThread: 0x7a25f5a0>{number = 1, name = main} i = 1 2015-02-16 17:33:12.938 多线程初步[1574:121669] <NSThread: 0x7a25f5a0>{number = 1, name = main} i = 2
4. 并行队列的同步任务:只使用主线程依次执行。
Tip:串行队列异步任务的用处是最大的,既可以异步,又可以顺序执行。
Tip:如果先向队列中加入异步任务,再加入同步任务,同步任务会穿插在异步任务中运行。
Tip:并行队列难以控制执行顺序与最大并发数,容易出错。
5.线程名字的作用:打了断点后,可以在左侧看到线程名称。
Tip:通过点击CPU的运行状态还可以得到每个线程的CPU占用情况。
6.非ARC开发时,不要忘记写dispatch_release(q);
【全局队列】
Apple提供的,供所有App共同使用,它是一种特殊的并行队列。
使用dispatch_get_global_queue获取,第一个参数为优先级,通过右侧的提示输入即可,第二个是供以后使用的,传入0即可。
与并行队列的区别:不需要创建,名称以com.apple开头。
缺点:调试时无法得到准确队列的名称。
【主线程队列】
注意:iOS只能在主线程上更新UI,因此与UI更新有关的操作应该在主线程执行,它是一种特殊的串行队列。
使用dispatch_get_main_queue()函数获取。
与串行队列的区别:不需要创建,名称以com.apple开头。
【进程阻塞】
注意主线程是一直工作的,除非将程序杀掉,否则主线程的工作永远不会结束。
由于同步任务要等待前面的任务,因此在主线程中加入同步任务,会引起进程阻塞。
Tip:主线程中只能添加异步任务。
【小结】
1.并发编程对多线程编程进行了封装,不需要关心线程的创建与回收,是为了让程序员从复杂的线程控制中解脱出来,只需要面对队列和任务即可。
2.注意队列的优先级永远写DISPATCH_QUEUE_PRIORITY_DEFAULT,优先级出错时会造成优先级反转,低优先级的线程可能会阻塞高优先级的线程,为了保险起见,应该使用串行队列的异步任务。
3.同步任务嵌套同步任务,会引发阻塞,原因在于,第一个任务执行完毕的条件是第二个任务执行完毕,而第二个任务执行完毕的条件是第一个任务执行完毕,因此二者相互等待,无法结束。
4.GCD队列有5个优先级,优先级从高到底分别为Main->High->Default->Low->Background,后面四级都放入GCD线程池中,最高级的即为主线程。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。