参考学习https://www.dreamingwish.com/article/grand-central-dispatch-basic-1.html系列文章,貌似也是翻译自他处的。以为很是完整,就本身搬运过来,备忘和分享。防止之后遇到问题, 想起来,可是又找不到的痛苦html
什么是GCD?git
Grand Central Dispatch或者GCD,是一套低层API,提供了一种新的方法来进行并发程序编写。从基本功能上讲,GCD有点像NSOperationQueue,他们都容许程序将任务切分为多个单一任务而后提交至工做队列来并发地或者串行地执行。GCD比之NSOpertionQueue更底层更高效,而且它不是Cocoa框架的一部分。github
除了代码的平行执行能力,GCD还提供高度集成的事件控制系统。能够设置句柄来响应文件描述符、mach ports(Mach port 用于 OS X上的进程间通信)、进程、计时器、信号、用户生成事件。这些句柄经过GCD来并发执行。编程
GCD的API很大程度上基于block,固然,GCD也能够脱离block来使用,好比使用传统c机制提供函数指针和上下文指针。实践证实,当配合block使用时,GCD很是简单易用且能发挥其最大能力。安全
你能够在Mac上敲命令“man dispatch”来获取GCD的文档。多线程
为什么使用?并发
GCD提供不少超越传统多线程编程的优点:框架
Dispatch Objects异步
尽管GCD是纯c语言的,但它被组建成面向对象的风格。GCD对象被称为dispatch object。Dispatch object像Cocoa对象同样是引用计数的。使用dispatch_release和dispatch_retain函数来操做dispatch object的引用计数来进行内存管理。但主意不像Cocoa对象,dispatch object并不参与垃圾回收系统,因此即便开启了GC,你也必须手动管理GCD对象的内存。async
Dispatch queues 和 dispatch sources(后面会介绍到)能够被挂起和恢复,能够有一个相关联的任意上下文指针,能够有一个相关联的任务完成触发函数。能够查阅“man dispatch_object”来获取这些功能的更多信息。
Dispatch Queues
GCD的基本概念就是dispatch queue。dispatch queue是一个对象,它能够接受任务,并将任务以先到先执行的顺序来执行。dispatch queue能够是并发的或串行的。并发任务会像NSOperationQueue那样基于系统负载来合适地并发进行,串行队列同一时间只执行单一任务。
GCD中有三种队列类型:
dispatch_queue_create
建立的队列. 这些队列是串行的。正由于如此,它们能够用来完成同步机制, 有点像传统线程中的mutex。建立队列
要使用用户队列,咱们首先得建立一个。调用函数dispatch_queue_create就好了。函数的第一个参数是一个标签,这纯是为了debug。Apple建议咱们使用倒置域名来命名队列,好比“com.dreamingwish.subsystem.task”。这些名字会在崩溃日志中被显示出来,也能够被调试器调用,这在调试中会颇有用。第二个参数目前还不支持,传入NULL就好了。
提交 Job
向一个队列提交Job很简单:调用dispatch_async函数,传入一个队列和一个block。队列会在轮到这个block执行时执行这个block的代码。下面的例子是一个在后台执行一个巨长的任务:
//1.提交 Job //优先级为Default dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //global_queue会在轮到这个block执行时执行这里的代码 [self goDoSomethingLongAndInvolved]; NSLog(@"Done doing something long and involved"); });
dispatch_async
函数会当即返回, block会在后台异步执行。 因此后面的代码跟block里面的代码的执行顺序不肯定
固然,一般,任务完成时简单地NSLog个消息不是个事儿。在典型的Cocoa程序中,你颇有可能但愿在任务完成时更新界面,这就意味着须要在主线程中执行一些代码。你能够简单地完成这个任务——使用嵌套的dispatch,在外层中执行后台任务,在内层中将任务dispatch到main queue:
//2.使用嵌套的dispatch,异步抛任务给后台执行,后台异步执行完任务以后,dispatch回到main queue,执行更新UI dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //异步执行操做序列 [self goDoSomethingLongAndInvolved]; //异步执行完任务以后须要回到main_queue作UI页面更新等等 dispatch_async(dispatch_get_main_queue(), ^{ // [self.sumLabel setText:[NSString stringWithFormat:@"%ld",self.sum]]; }); });
dispatch_sync,同步执行,等待block中的代码执行完成并返回,而后执行接下来的任务。用 __block类型修饰符,能够用来从执行中的block获取一个值。例如,你可能有一段代码异步抛到后台去执行,而它须要从界面控制层获取一个值。那么你可使用dispatch_sync在异步操做中暂停一下,而后作完这个同步操做,取到UI上的值,而后再进行异步操做的接下来代码序列:
#pragma mark - 适合异步抛给后台作的事情 -(void)goDoSomethingLongAndInvolved{ NSInteger sum = 0; for (int i = 0; i<50000; i++) { sum = sum + i; } //3.dispatch_sync同步操做 将同步操做放在异步抛给后台执行的代码中,就能够,先等这个同步操做执行完以后,再接着进行下面的异步操做序列
//这个有阻塞了后台的线程
__block NSString *stringValue; dispatch_sync(dispatch_get_main_queue(), ^{ //获取主线程里的UI中的Label的值,累加上Label的值 stringValue = self.sumLabel.text; NSLog(@"实现累加上次结果的功能"); }); sum = sum + stringValue.integerValue; self.sum = sum; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. }
上面的效果也能够这样实现:
//用嵌套的block来停止后台线程,而后从主线程中获取值,而后再将后期处理提交至后台线程: dispatch_queue_t bgQueue = myQueue; dispatch_async(dispatch_get_main_queue(), ^{ NSString *stringValue = sumLabel.text; dispatch_async(bgQueue, ^{ // use stringValue in the background now }); }); //取决于你的需求,myQueue能够是用户队列也可使全局队列。
再也不使用锁(Lock)
用户队列能够用于替代锁来完成同步机制。在传统多线程编程中,你可能有一个对象要被多个线程使用,你须要一个锁来保护这个对象:
NSLock *lock;
访问代码会像这样:
- (id)something { id localSomething; [lock lock]; localSomething = [[something retain] autorelease]; [lock unlock]; return localSomething; } - (void)setSomething:(id)newSomething { [lock lock]; if(newSomething != something) { [something release]; something = [newSomething retain]; [self updateSomethingCaches]; } [lock unlock]; }
使用GCD,可使用queue来替代锁:
dispatch_queue_t queue;
要用于同步机制,queue必须是一个用户队列(从OS X v10.7和iOS 4.3开始,还必须指定为DISPATCH_QUEUE_SERIAL),而非全局队列,因此使用dispatch_queue_create
初始化一个。而后能够用dispatch_async
或者 dispatch_sync
将共享数据的访问代码封装起来:
- (id)something { __block id localSomething; dispatch_sync(queue, ^{ localSomething = [something retain]; }); return [localSomething autorelease]; } - (void)setSomething:(id)newSomething { dispatch_async(queue, ^{ if(newSomething != something) { [something release]; something = [newSomething retain]; [self updateSomethingCaches]; } }); }
使用GCD途径有几个好处:
-setSomething:是怎么使用dispatch_async的。调用 -setSomething:会当即返回,而后这一大堆工做会在后台执行。若是updateSomethingCaches是一个很费时费力的任务,且调用者将要进行一项处理器高负荷任务,那么这样作会很棒。
练习demo地址:https://github.com/Jordan150513/OCDemos.git中的GCDDemos项目