标签:
什么是GCD?
Grand Central Dispatch或者GCD,是?一套低层API,提供了?一种新的?方法来进?行并发程序编写。从基本功能上讲,GCD有点像NSOperationQueue,他们都允许程序将
任务切分为多个单?一任务然后提交?至?工作队列来并发地或者串?行地执?行。GCD?比之NSOpertionQueue更底层更?高效,并且它不是Cocoa框架的?一部分。 除了代码的平?行执?行能?力,GCD还提供?高度集成的事件控制系统。可以设置句柄来响应?文件描述符、mach ports(Mach port ?用于 OS X上的进程间通讯)、进程、计时
器、信号、?用户?生成事件。这些句柄通过GCD来并发执?行。 GCD的API很?大程度上基于block,当然,GCD也可以脱离block来使?用,?比如使?用传统c机制提供函数指针和上下?文指针。实践证明,当配合block使?用时,GCD?非常简
单易?用且能发挥其最?大能?力。
你可以在Mac上敲命令“man dispatch”来获取GCD的?文档。
为何使?用?
GCD提供很多超越传统多线程编程的优势:
易?用: GCD?比之thread跟简单易?用。由于GCD基于work unit?而?非像thread那样基于运算,所以GCD可以控制诸如等待任务结束、监视?文件描述符、周期执?行代码以及 ?工作挂起等任务。基于block的?血统导致它能极为简单得在不同代码作?用域之间传递上下?文。
效率: GCD被实现得如此轻量和优雅,使得它在很多地?方?比之专?门创建消耗资源的线程更实?用且快速。这关系到易?用性:导致GCD易?用的原因有?一部分在于你可以不 ?用担?心太多的效率问题?而仅仅使?用它就?行了。
性能: GCD?自动根据系统负载来增减线程数量,这就减少了上下?文切换以及增加了计算效率。
Dispatch Objects
尽管GCD是纯c语?言的,但它被组建成?面向对象的?风格。GCD对象被称为dispatch object。Dispatch object像Cocoa对象?一样是引?用计数的。使?用dispatch_release和 dispatch_retain函数来操作dispatch object的引?用计数来进?行内存管理。但注意不像Cocoa对象,dispatch object并不参与垃圾回收系统,所以即使开启了GC,你也必须?手 动管理GCD对象的内存。
Dispatch queues 和 dispatch sources(后?面会介绍到)可以被挂起和恢复,可以有?一个相关联的任意上下?文指针,可以有?一个相关联的任务完成触发函数。可以查 阅“man dispatch_object”来获取这些功能的更多信息。
Dispatch Queues
GCD的基本概念就是dispatch queue。dispatch queue是?一个对象,它可以接受任务,并将任务以先到先执?行的顺序来执?行。dispatch queue可以是并发的或串?行的。 并发任务会像NSOperationQueue那样基于系统负载来合适地并发进?行,串?行队列同?一时间只执?行单?一任务。
GCD中有三种队列类型:
The main queue: 与主线程功能相同。实际上,提交?至main queue的任务会在主线程中执?行。main queue可以调?用dispatch_get_main_queue()来获得。因为main queue是与主线程相关的,所以这是?一个串?行队列。
Global queues: 全局队列是并发队列,并由整个进程共享。进程中存在三个全局队列:?高、中(默认)、低、后台四个优先级队列。可以调?用 dispatch_get_global_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
?户队列: 用户队列 (GCD并不这样称呼这种队列, 但是没有?一个特定的名字来形容这种队列,所以我们称其为?用户队列) 是?用函数 dispatch_queue_create 创建的 队列. 这些队列是串?行的。正因为如此,它们可以?用来完成同步机制, 有点像传统线程中的mutex。
创建队列
要使用户队列,我们?首先得创建?一个。调?用函数dispatch_queue_create就?行了。函数的第?一个参数是?一个标签,这纯是为了debug。Apple建议我们使?用倒置域名来 命名队列,?比如“com.dreamingwish.subsystem.task”。这些名字会在崩溃?日志中被显?示出来,也可以被调试器调?用,这在调试中会很有?用。第?个参数??目前还不?支持,传?入 NULL就?行了。
提交 Job 向?一个队列提交Job很简单:调?用dispatch_async函数,传?入?一个队列和?一个block。队列会在轮到这个block执?行时执?行这个block的代码。下?面的例?子是?一个在后台执
?行?一个巨?长的任务:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self goDoSomethingLongAndInvolved]; NSLog(@"Done doing something long and involved");
});
码。你可以简单地完成这个任务——使?用嵌套的dispatch,在外层中执?行后台任务,在内层中将任务dispatch到main queue:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self goDoSomethingLongAndInvolved]; dispatch_async(dispatch_get_main_queue(), ^{
[textField setStringValue:@"Done doing something long and involved"]; });
});
还有一个函数叫dispatch_sync,它干的事?儿和dispatch_async相同,但是它会等待block中的代码执?行完成并返回。结合 __block类型修饰符,可以?用来从执?中的 block获取?一个值。例如,你可能有?一段代码在后台执?行,?而它需要从界?面控制层获取?一个值。那么你可以使?用dispatch_sync简单办到:
__block NSString *stringValue; dispatch_sync(dispatch_get_main_queue(), ^{
// __block variables aren‘t automatically retained // so we‘d better make sure we have a reference we can keep stringValue = [[textField stringValue] copy];
}); [stringValue autorelease]; // use stringValue in the background now
我们还可以使?用更好的?方法来完成这件事——使?用更“异步”的?风格。不同于取界?面层的值时要阻塞后台线程,你可以使?用嵌套的block来中?止后台线程,然后从主线程中 获取值,然后再将后期处理提交?至后台线程:
dispatch_queue_t bgQueue = myQueue; dispatch_async(dispatch_get_main_queue(), ^{
NSString *stringValue = [[[textField stringValue] copy] autorelease]; 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必须是?一个?用户队列,?而?非全局队列,所以使?用usingdispatch_queue_create初始化?一个。然后可以?用dispatch_async 或
dispatch_async 函数会?立即返回, block会在后台异步执?行。 当然,通常,任务完成时简单地NSLog个消息不是个事?儿。在典型的Cocoa程序中,你很有可能希望在任务完成时更新界?面,这就意味着需要在主线程中执?行?一些代
者 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];
值得注意的是dispatch queue是?非常轻量级的,所以你可以?大?用特?用,就像你以前使?用lock?一样。 现在你可能要问:“这样很好,但是有意思吗?我就是换了点代码办到了同?一件事?儿。” 实际上,使?用GCD途径有?几个好处:
1. 平行计算: 注意在第?二个版本的代码中, -setSomething:是怎么使?用dispatch_async的。调?用 -setSomething:会?立即返回,然后这?一?大堆?工作会在后台执 ?行。如果updateSomethingCaches是?一个很费时费?力的任务,且调?用者将要进?行?一项处理器?高负荷任务,那么这样做会很棒。
2. 安全: 使?用GCD,我们就不可能意外写出具有不成对Lock的代码。在常规Lock代码中,我们很可能在解锁之前让代码返回了。使?用GCD,队列通常持续运?行,你必将 归还控制权。
3. 控制: 使?用GCD我们可以挂起和恢复dispatch queue,?而这是基于锁的?方法所不能实现的。我们还可以将?一个?用户队列指向另?一个dspatch queue,使得这个?用户队列 继承那个dispatch queue的属性。使?用这种?方法,队列的优先级可以被调整——通过将该队列指向?一个不同的全局队列,若有必要的话,这个队列甚?至可以被?用来在 主线程上执?行代码。
4. 集成: GCD的事件系统与dispatch queue相集成。对象需要使?用的任何事件或者计时器都可以从该对象的队列中指向,使得这些句柄可以?自动在该队列上执?行,从?而使 得句柄可以与对象?自动同步。
总结
现在你已经知道了GCD的基本概念、怎样创建dispatch queue、怎样提交Job?至dispatch queue以及怎样将队列?用作线程同步。接下来我会向你展?示如何使?用GCD来编 写平?行执?行代码来充分利?用多核系统的性能^ ^。我还会讨论GCD更深层的东?西,包括事件系统和queue targeting。
标签:
原文地址:http://www.cnblogs.com/hanrychen/p/4539440.html