记录下关于学习和使用GCD的一些问题和理解。
GCD,Grand Central Dispatch的简称。首次发布在Mac OS X 10.6 ,iOS 4开始引入使用,是以纯C为底层的强大技术。其操作机制是基于线程上队列对于任务的调度执行,遵循FIFO(First In First Out)。
对于线程常用的三个方式的比较:
NSThead主要做简单的,缺点是无法管理线程的数量,需要手动管理线程。
// 开启并且执行一个线程
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
// 创建一个线程但是不会执行,需要手动调用
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument
// 通过通知中心监听此消息达到监听线程结束的目的
NSThreadWillExitNotification
NSOperation是基于OC的面向对象的多线程可以管理线程数,比GCD多了一些简单实用的功能。
// maxConcurrentOperationCount
设置最大开启的线程数,设置为1,则成为串行队列,否则,为并发队列
GCD是基于C的,效率高,会自动的管理线程周期(包括创建线程,任务处理,销毁线程)。
注意一个技巧:三种线程方式,都可以使用[NSThread currentThread]追踪任务所在的线程。
下面我们主要看下GCD的核心三个方面:线程、 队列 、任务(block执行块)。原理是将一段(Block)代码作为特定任务,放到指定的队列(queue:Serial(串行,同时只能执行一个任务)/globel(并行,同时可执行多个任务,但执行顺序是随机的)/main(全局的Serial,在主线程里面执行任务) dispatch queue)里,进行同步/异步(dispatch_sync/dispatch_async)处理。根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务。其中 线程分为:主线程、分线程(子线程)
队列分为:
(1)串行队列:自己手动创建的,队列中的任务只会顺序执行 (类似排队)(DISPATCH_QUEUE_SERIAL也可使用NULL代替)。
dispatch_queue_t q = dispatch_queue_create(“....”, DISPATCH_QUEUE_SERIAL);
(2)并行队列:自己手动创建的,队列中的任务通常会并发执行(类似赛跑)
dispatch_queue_t q = dispatch_queue_create("......", DISPATCH_QUEUE_CONCURRENT);
( 3)系统提供的几个并发队列。他们叫做全局调度队列(Global Dispatch Queues) ,一般进程中存在三个全局队列。全局队列有着依次从低到高的四个不同优先级:background、low、default 以及 high。PS:Apple 的 API 也会使用这些队列,所以你添加的任何任务都不会是这些队列中唯一的任务。
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
(4) 主队列,提交至main queue的任务会在主线程中执行,是一个默认的串行队列。
dispatch_queue_t q = dispatch_get_main_queue();
处理线程中的队列任务,我们需要记得两句话:同步异步(dispatch_sync/dispatch_async)决定开不开线程,串行并行决定开几条线程。
关于我们在实际开发中,利用GCD去做一些线程处理时,常用方法有下面几种简单的使用:
// 异步执行:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// something
});
// 主线程执行:
dispatch_async(dispatch_get_main_queue(), ^{
// something
});
// 一次性执行:
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// code to be executed once
});
// 延迟2秒执行:
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// code to be executed on the main queue after delay
});
// 自定义dispatch_queue_t
dispatch_queue_t urls_queue = dispatch_queue_create("blog.LL86.com", NULL);
dispatch_async(urls_queue, ^{
// your code
});
dispatch_release(urls_queue);
// 快速迭代遍历
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){
// 执行10次代码,index顺序不确定
});
//常用结构
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 耗时的操作
dispatch_async(dispatch_get_main_queue(), ^{
// 主线程刷新UI
});
});
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行1个耗时的异步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行1个耗时的异步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步操作都执行完毕后,回到主线程...
});
其中需要注意的是:
dispatch_after 是 延迟提交任务,并不是延迟多少秒后立即执行。在使用GCD做定时源时,使用dispatch_source_create创建一个定时器并绑定到主分派队列上。GCD定时器是基于分派队列,而不是像NSTimer那样基于运行循环,这意味着把他们用在多线程应用中要容易的多。
dispatch_suspend(queue) 挂起队列,并不是立即暂停正在运行的block任务,而是在当前block任务执行结束以后,暂停后续的block运行。\-。-/~
dispatch_sync 都不陌生的是,这句代码本身就是要放入主线程中运行的,因此有时不注意的一些操作会常造成我们常说的死锁现象(也就是线程相互等待现象)。