您的位置:首页 > 移动开发 > IOS开发

GCD

2016-03-19 17:23 435 查看
<span style="font-family: 'Helvetica Neue'; background-color: rgb(255, 255, 255);">GCD(Grand Central Dispatch)——iOS 4引入的技术</span>


1.线程的一些基础知识

进程:一个正在运行的程序可以看做一个进程。
线程:程序中独立运行的代码段。

一个进程是由至少一个的线程组成,进程只负责资源的调度和分配,线程才是程序真正的执行单元。
在单核 CPU 时代,CPU只能处理1条线程,只有1条线程在工作
多线程并发执行,其实是CPU快速地在多条线程之间调度(切换)
如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象来实现软件层面的多线程。创建线程,线程间切换都是有成本开销的。但由于多线程可以避免阻塞所造成的
CPU 计算时间浪费,所以多线程所带来的开销成本总体看来是值得的。任务一般都可以被拆分成多个子任务,如果一个子任务发生了阻塞,计算时间就可以分配给其他子任务。这样就提高了 CPU 的利用率。 而基于XNU内核的iOS在发生操作系统时间时会切换执行路径,将CPU的寄存器等信息保存在专用的内存块中,复原时再从内存块中复原CPU的寄存器等信息信息,继续执行CPU指令。
但是现在的物理CPU芯片一般都是有64个(64核)cpu。由于硬件上就支持多线程技术,就可以让多个线程真正同时地运行。从而真的提供了多个CPU核并行执行多个线程的技术。

多线程的目的是,通过并发执行提高 CPU 的使用效率,进而提供程序运行效率。

2.iOS中的多线程
而iOS中的多线程API有不少,大体有下面几个
NSOperation(基于GCD的OC封装,对于初学者来说接口更友好,性能比GCD差点)
NSThread(用的比较少,一般也只会做一些简单的逻辑,)
GCD(iOS中多线程的绝对主力,简单方便功能强大,也是这篇文章说的重点)
3.GCD
1.Dispatch Queue (执行处理的等待队列):Dispatch Queue
的执行顺序是FIFO,包含两种
1>Serial Dispatch Queue -串行(等待现在执行中处理结束)
2>Concurrent Dispatch Queue -并行(不等待现在执行中的处理结束)

4.GCD具体方法

1>创建dispatch_queue_create

dispatch_queue_t OneserialDispatchQueue = dispatch_queue_create(NULL, NULL);
这是创建一个标示为空的串行队列。创建具体的队列和实现场景直接上代码
//Serial Dispatch Queue的使用场景一般是在处理数据竞争类似的问题
// Serial Dispatch Queue 虽然是串行队列,但是可以创建多个,然后将执行添加到多个Serial Dispatch Queue中,其执行方式是并发的
// 第一个参数:队列的标识,---该参数也可以是NULL,但是建议使用程序ID因为在调试和CrashLog中可以方便识别
//第二个参数:队列的类型(串行/并行) ---可以是NULL那么就是串行 NULL = DISPATCH_QUEUE_SERIAL / DISPATCH_QUEUE_CONCURRENT(并行)
dispatch_queue_t OneserialDispatchQueue = dispatch_queue_create(NULL, NULL);
dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t TwoserialDispatchQueue = dispatch_queue_create("TwoserialDispatchQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t ThreeserialDispatchQueue = dispatch_queue_create("ThreeserialDispatchQueue", NULL);
dispatch_async(OneserialDispatchQueue, ^{
sleep(2);
NSLog(@"1=");
});
dispatch_async(TwoserialDispatchQueue, ^{
sleep(2);
NSLog(@"2=");
});
dispatch_async(ThreeserialDispatchQueue, ^{
sleep(2);
NSLog(@"3=");
});

dispatch_async(OneserialDispatchQueue, ^{
sleep(2);
NSLog(@"4=");
});
dispatch_async(concurrentDispatchQueue, ^{
sleep(2);
NSLog(@"5=");
});
NSLog(@"%@",OneserialDispatchQueue);

// 执行顺序应该是1、2、3、5三个并发先执行(1、2、3、5的执行顺序可能每次都不一样因为是并发,GCD的FIFO指的是同一个线程),4肯定晚于1执行(FIFO原则)。因为1、2、3、5(5是并发队列)是在三个不同的串行队列中然后异步加入队列,4和1是加入到同一个一个串行中,所以必然等到1执行完成后再执行

/*
在MRC和ARC的iOS6之前(这两种情况现在非常少,我是没见过适配iOS6之前的)我们要对GCD进行内存管理,使用下面的方法。

dispatch_release(OneserialDispatchQueue);
dispatch_retain(OneserialDispatchQueue);

*/

// iOS6之后GCD已经进入ARC的管理了,我们就不用管这个了
2> 获取系统的Dispatch Queue

工作中我们大多数情况下不用自己创建Dispatch Queue,我们可以方便的获取系统标准提供的Dispatch Queue那就是下面的两个

dispatch_queue_t main = dispatch_get_main_queue();


如其名指的就是主线程(更新UI必须在此线程)所以也肯定是 Serial Dispatch Queue它的处理在主线程的RunLoop中执行
还有就是dispatch_get_global_queue
/*
dispatch_get_global_queue是 concurrent Dispatch Queue
第一个参数有四个优先级

#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

再向Global Dispatch Queue 中追加处理时,应选择与处理内容对应的优先级,但是XNU内核用于Global Dispatch Queue的线程并不能保证实时性,只是大致判断
*/
dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // dispatch_queue_create创建的就是DISPATCH_QUEUE_PRIORITY_DEFAULT这种优先级
3>变更优先级:dispatch_set_target_queue
这个一般都用不到

dispatch_queue_t serialDispatchQueue = dispatch_queue_create(NULL, NULL);
dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
/*
变更生成的Dispatch Queue的执行优先级
第一个参数:要变更的Dispatch Queue
第二个参数;目标Dispatch Queue
*/
dispatch_set_target_queue(serialDispatchQueue, globalDispatchQueue);


4>延时执行dispatch_after

// 作用:在指定时间后执行处理
// 注意:dispatch_after并不是在指定时间后执行处理,而只是在指定时间追加处理到Dispatch Queue,如果在每隔1/60秒执行的RunLoop中,可能会在time+1/60秒后执行甚至更久。

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(time * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"dispatch_after执行");
});


5>dispatch_group_t

这个比较重要也不是很好理解,就多讲点
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 1创建dispatch_group_t
dispatch_group_t group = dispatch_group_create();
/* 第一个参数:dispatch_group_t
第二个参数:要执行的block的Dispatch Queue 加入group的线程可以是任意的,主线程子线程都可以,
*/
dispatch_group_async(group, queue, ^{
NSLog(@"任务1");
});
dispatch_group_async(group, queue, ^{
sleep(1);
NSLog(@"任务2");
});
dispatch_group_async(group, queue, ^{
sleep(2);
NSLog(@"任务3");
});
dispatch_group_async(group, queue, ^{
NSLog(@"任务4");
});
// 3,在所有加到group的任务执行完成后,再执行此任务,无论上边的queue是否一致,
还是上边的Queue和dispatch_group_notify中的Queue是否一致,下边的block总是在上边的执行完成后才执行
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"最后任务");
});
NSLog(@"dispatch_group_notify是异步执行");
// 还有一种用法是将dispatch_group_notify换成dispatch_group_wait,但是dispatch_group_wait会堵塞当前线程,直到等待时间到达或者group执行完成
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);
// 这个函数是有返回值的

#warning 最好不要轻易使用dispatch_group_wait,因为会阻塞线程,而且代码也会复杂
long result = dispatch_group_wait(group, time);
if (result == 0) {
NSLog(@"group中的全部处理完成");

}else{
NSLog(@"group中还有处理在进行");

}
NSLog(@"dispatch_group_wait阻塞了当前线程");

dispatch_group_t手动管理group关联的block的运行状态(或计数),进入和退出group次数必须匹配

dispatch_group_enter(group);
dispatch_group_leave(group);
dispatch_group_enter(group);
一般这样实现
dispatch_group_async(group, queue, ^{

});


dispatch_async(queue, ^{
  dispatch_group_leave(group);
});
这两者等价
6>dispatch_barrier_async

dispatch_barrier_async函数会等待追加到Concurrent Dispatch Queue(串行对于此函数是没有意义的)上的并行执行的处理全部结束之后,再执行dispatch_barrier_async中的block,此block执行完成后,Concurrent
Dispatch Queue的处理又开始并行执行
此函数在处理数据竞争时是非常有用的,比如读数据时不能写数据,写数据时不能读,读数据可以一块执行
dispatch
a364
_barrier_async只能和dispatch_queue_create创建的Queue一起使用,和dispatch_get_global_queue创建的函数一块使用是有问题的.
 dispatch_barrier_async在 一般在自定义并发队列使用
//    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
sleep(2);
NSLog(@"读数据1");
});
dispatch_async(queue, ^{
sleep(1);
NSLog(@"读数据2");
});
dispatch_async(queue, ^{
sleep(3);
NSLog(@"读数据3");
});

dispatch_barrier_async(queue, ^{
NSLog(@"写数据dispatch_barrier_async任务");
});

dispatch_async(queue, ^{
sleep(2);
NSLog(@"读数据4");
});
dispatch_async(queue, ^{
sleep(1);
NSLog(@"读数据5");
});


7>dispatch_sync

如其名,就是同步,就是等待当现程处理结束时才执行
我们在主线程需要更新UI,但是在数据没有处理完时不能更新,处理完时要立即更新,所以可以使用此种写法
NSLog(@"开始");

dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
NSLog(@"处理");
});

NSLog(@"完结");
#warning dispatch_sync使用时要格外注意,因为很容易造成死锁
//dispatch_sync,在指定处理没有完成时是不会返回的,下面这种就会造成死锁,原因是主线程在等待block的执行,而无法去执行block
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"死锁");
});
NSLog(@"永远不会执行");


8>dispatch_suspend
当想要让一个队列的执行暂时挂起可以调用dispatch_suspend,

dispatch_queue_t queue = dispatch_queue_create("dispatchSuspend", NULL);
// 挂起指定的 Queue
dispatch_suspend(queue);
// 恢复指定的 Queue
dispatch_resume(queue);


9>dispatch_semaphore_t信号量

主要也是解决数据竞争等问题

// 创建semaphore 参数标示计数初始值
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
// 等待semaphore 当参数计数为0时等待,为1或者大于1时,则减去1而不等待
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
/*
第二个参数也可以设置成为一段时间,当返回值是0的时候,说明在指定时间内数值达到大于等于1或者semaphore达到大于等于1
*/
long result = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC));
if (result == 0) {
// codeing
}else{
// 时间到,semaphore数值还是0
}

//semaphore 数值加1
dispatch_semaphore_signal(semaphore);

10>dispatch_suspend队列挂起

dispatch_queue_t queue = dispatch_queue_create("dispatchSuspend", NULL);
// 挂起指定的 Queue
dispatch_suspend(queue);
// 恢复指定的 Queue
dispatch_resume(queue);
11>dispatch_once_t

// 线程安全的只执行一次指定处理,最主要的作用就是创建单例,怎么创建应该做过iOS开发的都知道
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// code to be executed once
});


总结:1.GCD是iOS多线程开发中最常用的技术,功能强大。但是也不要滥用,因为会让你和你的同事在后面的维护上增加难度。所以不要滥用

2.GCD的实现有时候并不能完全按着我们的想法执行,因为苹果会在底层进行自己的优化,比如:

dispatch_queue_t myQueue = dispatch_queue_create("com.team", NULL);
dispatch_sync(myQueue, ^{
NSLog(@"task2 == %@",[NSThread currentThread]);
});
task2按理应该在myQueue中执行,但是它实际在main queue中执行。具体原因可以看一下这个原因。但是这并不影响我们的使用。

3.学好GCD,妈妈再也不担心我的多线程了!!!!!!!!!!!!

文中demo:点击打开
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ios gcd dispatch