身为开发者的我们,总是会面临着各种各样的需求.必不可少的一个硬性规则:从用户体验的角度来开发项目!那么这就要求我们必须减少用户在使用程序中所需要等待的时间,看吧,多线程的概念出来了.
我们经常在敲代码的时候边敲边听音乐,看看,这就是一个多线程的体现了(如果敲代码的时候不能进行其他的操作,我们会怎么想?).通过一张图我们来看一下实现进程的必要性吧
在iOS中,每启动一个进程都会创建一个主线程(一般用于刷新UI),这个线程是其他线程的父线程.由于在iOS中,除了主线程,其他的线程都是独立于Cocoa Touch的,所以只有主线程可以更新UI(不过现在好像其他线程也可能会成功,但是不推荐).常用的多线程开发主要有三种:
1.NSThread
2.NSOperation
3.GCD
这三种是随着iOS的发展逐渐引进来的,所以靠后的使用相对来说越简单,而且GCD也是Apple官方主推荐的.在这里我们也只讨论GCD.
GCD全称(Grand Central Dispatch),他是基于在C语言上的,抽象成都很高,使用也非常的简单.先说下GCD的优点吧.
1.GCD是Apple官方为多核并发提供的解决方案.
2.GCD会自动利用CPU内核(不需要我们关心)
3.GCD会自动管理线程的生命周期(不用我们手动管理)
4.总的来说就是我们只需要告诉GCD我们要执行什么任务,其他的都不用我们管理
注意:GCD是纯C语言的,我们在用GCD的时候,面对的是函数,而不是方法.
GCD中有两个核心的概念:
1.任务(我们要执行什么样的操作)
2.队列(专门用于存放任务的)
我们将任务添加到队列中,GCD会自动的将队列里的任务取出,放到对应的线程执行(FIFO).这几乎不用我们关心.
GCD的任务分为两种:
1.同步执行:dispatch_sync(dispatch_queue_t queue, dispatch_block_t block)
queue:队列
block:任务
把任务放进队列中执行
2.异步执行:dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
同步和异步的区别:
同步:在当前线程中执行(不具备开线程的能力)
异步:开辟新的线程执行任务(具备开线程能力,具体开几条暂时不说,下面会说)
GCD的队列也有两种:
串行队列:dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);(主线程dispatch_get_main_queue(void)也是特殊的串行队列)
第一个参数:队列名称
第二个参数:队列类型
并发队列:也是使用dispatch_queue_create(const char *label, dispatch_queue_attr_t attr),不过系统已经为我们准备好了一个全局的并发队列dispatch_get_global_queue(long identifier, unsigned long flags),一般不需要我们去创建
串行和并发的区别:
串行:一个一个的执行任务,遵守FIFO(先进先出)规则
并发:允许同时执行多个任务
( 注:dispatch_queue_attr_t attr决定着队列的类型
DISPATCH_QUEUE_SERIAL(NULL)串行队列(其中串行队列的宏值为NULL,所以可以用NULL来创建,像这样:dispatch_queue_create(@"NICAI", NULL),代表的是串行队列)
DISPATCH_QUEUE_CONCURRENT并发队列)
并发队列的优先级(long identifier):
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台
有很多人会混淆串行,并发,异步,同步的概念.当然,他们是单独开辟线程执行任务,还是唯一线程执行任务,并不绝对.例如:在串行异步,并不会开开辟新的线程.看图:1.2
从上面我们可以得出几个规律:
1.同步任务不会开辟新的线程(不管是串行队列还是并发队列)
2.异步任务只有在主线程的时候不会开辟新的线程,其他的都会开辟
3.并发队列只有在异步的时候才会并发的执行任务(也就是开多条线程,具体数量受系统控制,主线程除外)
下面我们用代码来一一验证:
1.全局队列异步执行:(1.3,1.4)
结论:全局队列异步执行同时开启了三个线程(number分别为3,4,2,)并发的执行(并不是按照FIFO打印出1,2,3)[第一个红色的框里面是项目中打印出来的数字,第二个红色框里面number代表着线程的数量,创建的时候是累加,name代表着线程的名字]
2.全局队列同步执行:(2.1,2.2)
结论:全局队列同步执行没有开启新的线程,在主线程执行任务
3.串行队列同步执行:(3.1,.3.2)
结论:并不会创建新的线程,在主线程中执行
3.串行队列异步执行:(3.1,.3.2)
结论:会开辟新的线程,但只开辟了一个
最后在总结一下:
1.在GDC中一个操作是多线程执行还是单线程执行取决于当前队列类型和执行方法,只有队列类型为并行队列并且使用异步方法执行时才能在多个线程中执行。
2.串行队列可以按顺序执行,并行队列的异步方法无法确定执行顺序。
3.UI界面的更新最好采用同步方法,其他操作采用异步方法。
GCD执行任务的方法并非只有简单的同步调用方法和异步调用方法,还有其他一些常用方法:
dispatch_apply():重复执行某个任务,但是注意这个方法没有办法异步执行(为了不阻塞线程可以使用dispatch_async()包装一下再执行)。
dispatch_once():单次执行一个任务,此方法中的任务只会执行一次,重复调用也没办法重复执行(单例模式中常用此方法)。
dispatch_time():延迟一定的时间后执行。
dispatch_barrier_async():使用此方法创建的任务首先会查看队列中有没有别的任务要执行,如果有,则会等待已有任务执行完毕再执行;同时在此方法后添加的任务必须等待此方法中任务执行后才能执行。(利用这个方法可以 控制执行顺)
dispatch_group_async():实现对任务分组管理,如果一组任务全部完成可以通过dispatch_group_notify()方法获得完成通知(需要定义dispatch_group_t作为分组标识)。