您的位置:首页 > 产品设计 > UI/UE

GCD介绍(一):基本概念和dispatch queues

2014-09-05 18:24 169 查看
转载自:http://mobile.51cto.com/iphone-402981.htm,尊重原创!

前言:之前开发Android程序时,我们知道,Looper是线程的消息队列循环,都是先进先出。线程所用的主要是newThread{},线程间通信采用handler。

ios的多线程有多种实现,如NSThread,NSOperation,GCD等。

这篇文章主要讲解GCD。

正文:1、GCD是什么?

2、为什么?

3、怎么用?

4、和NSThread等的区别。

一、基础概念

GCD,全称GrandCentralDispatch。

从基本功能上讲,GCD有点像NSOperationQueue,他们都允许程序将任务切分为多个单一任务然后提交至工作队列来并发地或者串行地执行。GCD比之NSOpertionQueue更底层更高效,并且它不是Cocoa框架的一部分。GCD创建的队列是轻量级的。

除了代码的平行执行能力,GCD还提供高度集成的事件控制系统。可以设置句柄来响应文件描述符、machports(Machport用于OSX上的进程间通讯)、进程、计时器、信号、用户生成事件。这些句柄通过GCD来并发执行。

GCD的API很大程度上基于block,当然,GCD也可以脱离block来使用,比如使用传统c机制提供函数指针和上下文指针。实践证明,当配合block使用时,GCD非常简单易用且能发挥其最大能力。

GCD是纯C语言的,但它被组建成面向对象的风格。GCD对象被称为dispatchobject。Dispatchobject像Cocoa对象一样是引用计数的。使用dispatch_release和dispatch_retain函数来操作dispatchobject的引用计数来进行内存管理。

注意:不同于Cocoa对象,dispatchobject并不参与垃圾回收系统,所以即使开启了GC,你也必须手动管理GCD对象的内存。

二、为什么?

1、易用:基于block,让它可以极为简单得在不同代码作用域之间传递上下文。且不必担心效率问题。

2、效率:GCD被实现得如此轻量和优雅,使得它在很多地方比之专门创建消耗资源的线程更实用且快速。

3、性能:GCD自动根据系统负载来增减线程数量,这就减少了上下文切换以及增加了计算效率。

三、使用

GCD的基本概念就是dispatchqueues。dispatchqueue是一个对象,它可以接受任务,并将任务以先到先执行的顺序来执行。dispatchqueue可以是并发的或串行的。

并发任务会像NSOperationQueue那样基于系统负载来合适地并发进行,串行队列同一时间只执行单一任务。

3.1创建队列

dispatchqueues有三种:全局并行队列,用户队列(串行队列),主队列

1、主队列mainqueue

与主线程功能相同,串行的。此队列的任务和应用程序主循环runloop的任务交替执行。

获得:dispatch_get_main_queue()

2、全局并行队列globalqueues

由整个进程共享,并行。系统给每个应用程序提供3个不同优先级的全局队列。不需要自己retain,release。

获得:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

优先级还有HIGH,LOW

3、串行队列(用户队列)

由用户自行创建,自己通过dispatch_retain()和dispatch_release()来实现引用计数。在同一时间只执行一个任务,可以使用串行队列代替锁来保护共享的数据,完成同步机制。

  获得:dispatch_queue_create("队列名字",NULL);苹果推荐使用倒置域名给队列命名。

3.2提交任务

分异步和同步

1、异步方法

dispatch_async(dispatch_queue_tqueue,dispatch_block_tblock);

函数会立即返回,block会在后台异步执行。

举例:在典型的Cocoa程序中,你很有可能希望在任务完成时更新界面,这就意味着需要在主线程中执行一些代码。你可以简单地完成这个任务——使用嵌套的dispatch,在外层中执行后台任务,在内层中将任务dispatch到mainqueue:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
[selfgoDoSomethingLongAndInvolved];
dispatch_async(dispatch_get_main_queue(),^{
[textFieldsetStringValue:@"Donedoingsomethinglongandinvolved"];
});
});


2、同步方法

dispatch_sync(dispatch_queue_tqueue,dispatch_block_tblock);

等待block中的代码执行完成并返回,结合__block类型修饰符,可以用来从执行中的block获取一个值。

举例:可能有一段代码在后台执行,而它需要从界面控制层获取一个值

__blockNSString*stringValue;
dispatch_sync(dispatch_get_main_queue(),^{
//__blockvariablesaren'tautomaticallyretained
//sowe'dbettermakesurewehaveareferencewecankeep
stringValue=[[textFieldstringValue]copy];
});
[stringValueautorelease];
//usestringValueinthebackgroundnow


我们还可以使用更好的方法来完成这件事——使用更“异步”的风格。不同于取界面层的值时要阻塞后台线程,你可以使用嵌套的block来中止后台线程,然后从主线程中获取值,然后再将后期处理提交至后台线程:

dispatch_queue_tbgQueue=myQueue;
dispatch_async(dispatch_get_main_queue(),^{
NSString*stringValue=[[[textFieldstringValue]copy]autorelease];
dispatch_async(bgQueue,^{
//usestringValueinthebackgroundnow
});
});


3.3使用用户队列实现同步机制

  用户队列可以用于替代锁来完成同步机制。在传统多线程编程中,你可能有一个对象要被多个线程使用,你需要一个锁来保护这个对象:

NSLock*lock;


访问代码会像这样:

-(id)something
{
idlocalSomething;
[locklock];
localSomething=[[somethingretain]autorelease];
[lockunlock];
returnlocalSomething;
}

-(void)setSomething:(id)newSomething
{
[locklock];
if(newSomething!=something)
{
[somethingrelease];
something=[newSomethingretain];
[selfupdateSomethingCaches];
}
[lockunlock];
}


使用GCD,可以使用queue来替代:

dispatch_queue_tqueue;


要用于同步机制,queue必须是一个用户队列,而非全局队列,所以使用

-(id)something
{
__blockidlocalSomething;
dispatch_sync(queue,^{
localSomething=[somethingretain];
});
return[localSomethingautorelease];
}

-(void)setSomething:(id)newSomething
{
dispatch_async(queue,^{
if(newSomething!=something)
{
[somethingrelease];
something=[newSomethingretain];
[selfupdateSomethingCaches];
}
});
}


值得注意的是dispatchqueue是非常轻量级的,所以你可以大用特用,就像你以前使用lock一样。

现在你可能要问:“这样很好,但是有意思吗?我就是换了点代码办到了同一件事儿。”

实际上,使用GCD途径有几个好处:

平行计算:注意在第二个版本的代码中,-setSomething:是怎么使用dispatch_async的。调用-setSomething:会立即返回,然后这一大堆工作会在后台执行。如果updateSomethingCaches是一个很费时费力的任务,且调用者将要进行一项处理器高负荷任务,那么这样做会很棒。

安全:使用GCD,我们就不可能意外写出具有不成对Lock的代码。在常规Lock代码中,我们很可能在解锁之前让代码返回了。使用GCD,队列通常持续运行,你必将归还控制权。

控制:使用GCD我们可以挂起和恢复dispatchqueue,而这是基于锁的方法所不能实现的。我们还可以将一个用户队列指向另一个dspatchqueue,使得这个用户队列继承那个dispatchqueue的属性。使用这种方法,队列的优先级可以被调整——通过将该队列指向一个不同的全局队列,若有必要的话,这个队列甚至可以被用来在主线程上执行代码。

集成:GCD的事件系统与dispatchqueue相集成。对象需要使用的任何事件或者计时器都可以从该对象的队列中指向,使得这些句柄可以自动在该队列上执行,从而使得句柄可以与对象自动同步。

总结

现在你已经知道了GCD的基本概念、怎样创建dispatchqueue、怎样提交Job至dispatchqueue以及怎样将队列用作线程同步。接下来我会向你展示如何使用GCD来编写平行执行代码来充分利用多核系统的性能^^。我还会讨论GCD更深层的东西,包括事件系统和queuetargeting。


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