什么是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提供很多超越传统多线程编程的优势:

  1. 易用: GCD比之thread更简单易用。由于GCD基于work unit而非像thread那样基于运算,所以GCD可以控制诸如等待任务结束、监视文件描述符、周期执行代码以及工作挂起等任务。基于block的血统导致它能极为简单得在不同代码作用域之间传递上下文。
  2. 效率: GCD被实现得如此轻量和优雅,使得它在很多地方比之专门创建消耗资源的线程更实用且快速。这关系到易用性:导致GCD易用的原因有一部分在于你可以不用担心太多的效率问题而仅仅使用它就行了。
  3. 性能: 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中有三种队列类型:

  1. The main queue: 与主线程功能相同。实际上,提交至main queue的任务会在主线程中执行。main queue可以调用dispatch_get_main_queue()来获得。因为main queue是与主线程相关的,所以这是一个串行队列。
  2. Global queues: 全局队列是并发队列,并由整个进程共享。进程中存在三个全局队列:高、中(默认)、低三个优先级队列。可以调用dispatch_get_global_queue函数传入优先级来访问队列。
  3. 用户队列: 用户队列 (GCD并不这样称呼这种队列, 但是没有一个特定的名字来形容这种队列,所以我们称其为用户队列) 是用函数 dispatch_queue_create 创建的队列. 这些队列是串行的。正因为如此,它们可以用来完成同步机制, 有点像传统线程中的mutex。

创建队列

要使用用户队列,我们首先得创建一个。调用函数dispatch_queue_create就行了。函数的第一个参数是一个标签,这纯是为了 debug。Apple建议我们使用倒置域名来命名队列,比如“com.dreamingwish.subsystem.task”。这些名字会在崩溃日 志中被显示出来,也可以被调试器调用,这在调试中会很有用。第二个参数目前还不支持,传入NULL就行了。

提交 Job

向一个队列提交Job很简单:调用dispatch_async函数,传入一个队列和一个block。队列会在轮到这个block执行时执行这个block的代码。下面的例子是一个在后台执行一个巨长的任务:

  1. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  2. [self goDoSomethingLongAndInvolved];
  3. NSLog(@"Done doing something long and involved");
  4. });

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

当然,通常,任务完成时简单地NSLog个消息不是个事儿。在典型的Cocoa程序中,你很有可能希望在任务完成时更新界面,这就意味着需要在主线 程中执行一些代码。你可以简单地完成这个任务——使用嵌套的dispatch,在外层中执行后台任务,在内层中将任务dispatch到main queue:

  1. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  2. [self goDoSomethingLongAndInvolved];
  3. dispatch_async(dispatch_get_main_queue(), ^{
  4. [textField setStringValue:@"Done doing something long and involved"];
  5. });
  6. });

还有一个函数叫dispatch_sync,它干的事儿和dispatch_async相同,但是它会等待block中的代码执行完成并返回。结合 __block类型修饰符,可以用来从执行中的block获取一个值。例如,你可能有一段代码在后台执行,而它需要从界面控制层获取一个值。那么你可以使 用dispatch_sync简单办到:

  1. __block NSString *stringValue;
  2. dispatch_sync(dispatch_get_main_queue(), ^{
  3. // __block variables aren't automatically retained
  4. // so we'd better make sure we have a reference we can keep
  5. stringValue = [[textField stringValue] copy];
  6. });
  7. [stringValue autorelease];
  8. // use stringValue in the background now

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

  1. dispatch_queue_t bgQueue = myQueue;
  2. dispatch_async(dispatch_get_main_queue(), ^{
  3. NSString *stringValue = [[[textField stringValue] copy] autorelease];
  4. dispatch_async(bgQueue, ^{
  5. // use stringValue in the background now
  6. });
  7. });

取决于你的需求,myQueue可以是用户队列也可以使全局队列。

不再使用锁(Lock)

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

  1. NSLock *lock;

访问代码会像这样:

  1. - (id)something
  2. {
  3. id localSomething;
  4. [lock lock];
  5. localSomething = [[something retain] autorelease];
  6. [lock unlock];
  7. return localSomething;
  8. }
  9. - (void)setSomething:(id)newSomething
  10. {
  11. [lock lock];
  12. if(newSomething != something)
  13. {
  14. [something release];
  15. something = [newSomething retain];
  16. [self updateSomethingCaches];
  17. }
  18. [lock unlock];
  19. }

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

  1. dispatch_queue_t queue;

要用于同步机制,queue必须是一个用户队列,而非全局队列,所以使用dispatch_queue_create初始化一个。然后可以用dispatch_async 或者 dispatch_sync将共享数据的访问代码封装起来:

  1. - (id)something
  2. {
  3. __block id localSomething;
  4. dispatch_sync(queue, ^{
  5. localSomething = [something retain];
  6. });
  7. return [localSomething autorelease];
  8. }
  9. - (void)setSomething:(id)newSomething
  10. {
  11. dispatch_async(queue, ^{
  12. if(newSomething != something)
  13. {
  14. [something release];
  15. something = [newSomething retain];
  16. [self updateSomethingCaches];
  17. }
  18. });
  19. }

值得注意的是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相集成。对象需要使用的任何事件或者计时器都可以从该对象的队列中指向,使得这些句柄可以自动在该队列上执行,从而使得句柄可以与对象自动同步。

ios专题 - GCD(1)的更多相关文章

  1. iOS多线程 GCD

    iOS多线程 GCD Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法. dispatch queue分成以下三种: 1)运行在主线程的Main que ...

  2. iOS's GCD Note

    [iOS's GCD Note] 1.默认有四种全局concureent queue,如下: 通过以下函数来引用: 2.官方文档上并发队列有3种,实际上main就是serial. 1)serial,用 ...

  3. ios专题 - GCD(2)

    何为Dispatch Sources 简单来说,dispatch source是一个监视某些类型事件的对象.当这些事件发生时,它自动将一个block放入一个dispatch queue的执行例程中. ...

  4. ios专题 - 多线程非GCD(1)

    iOS多线程初体验是本文要介绍的内容,iPhone中的线程应用并不是无节制的,官方给出的资料显示iPhone OS下的主线程的堆栈大小是1M,第二个线程开始都是512KB.并且该值不能通过编译器开关或 ...

  5. ios多线程-GCD基本用法

    ios中多线程有三种,NSTread, NSOperation,GCD 这篇就讲讲GCD的基本用法 平时比较多使用和看到的是: dispatch_async(dispatch_get_global_q ...

  6. iOS 多线程GCD的基本使用

    <iOS多线程简介>中提到:GCD中有2个核心概念:1.任务(执行什么操作)2.队列(用来存放任务) 那么多线程GCD的基本使用有哪些呢? 可以分以下多种情况: 1.异步函数 + 并发队列 ...

  7. ios开发--GCD使用介绍:4-延迟执行操作

    在开发过程中,我们有时会希望把一些操作封装起来延迟一段时间后再执行.iOS开发中,有两种常用的方法可以实现延迟执行,一种是使用GCD,另外一种是使用NSRunLoop类中提供的方法. 1.使用GCD实 ...

  8. iOS开发-GCD和后台处理

    一些生命周期函数的调用时间 打开应用时,调用 applicationWillEnterForeground: applicationDidBecomeActive: 按Home键,调用 applica ...

  9. iOS开发——GCD多线程详解

    GCD多线程详解 1. 什么是GCD Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,简单来说,GCD就是iOS一套解决多线程的机制,使用GCD能够最大限度简化多线程 ...

随机推荐

  1. python 实现对象模型

    # -*- coding:utf-8 -*- """ python 实现对象模型 创建 bmicalcpage 类 """ class bm ...

  2. suse系统卸载数据库实例

    1.停止数据库: 2.执行以下命令: find $ORACLE_BASE/* -name '*[Tt][Ee][Ss][Tt]*'  其中TEST为数据库的实例名: 删除存在的文件: 3.删除/etc ...

  3. 路径和 二叉树 leecode

    题目不难,很快ac,纯粹靠手感.https://oj.leetcode.com/problems/sum-root-to-leaf-numbers/ /** * Definition for bina ...

  4. vijosP1687 细菌总数

    vijosP1687 细菌总数 链接:https://vijos.org/p/1687 [思路] 错排公式+高精度. 题目要求排列数目而且不能有Pi==i的情况出现,可以看出这正是1,2,3,4,5, ...

  5. 【转】git - 简明指南

    git - 简明指南 助你入门 git 的简明指南,木有高深内容 ;) 作者:罗杰·杜德勒 感谢:@tfnico, @fhd 和 Namics其他语言 english, deutsch, españo ...

  6. DiscreteSeekBar---->SeekBar的使用

    build: compile 'org.adw.library:discrete-seekbar:1.0.0' 在布局中的使用: <org.adw.library.widgets.discret ...

  7. 使用CLRMD时通过Symbol Server找Dac的位置来初始化ClrRuntime

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:使用CLRMD时通过Symbol Server找Dac的位置来初始化ClrRuntime.

  8. [转]3proxy 二级代理配置样例

    转自:http://www.cnblogs.com/airsong23/p/3893094.html 适应情况: 有时,我们的机器HOST-A只能通过代理服务器HOST-B才可以访问internet, ...

  9. 苹果电脑出现 .DS_Store文件,删除不掉怎么处理?

    今天早上打开电脑,发现桌面上出现一个.DS_Store文件.可是出现的莫名其妙,也就没在意,可是重复删除之后还是出现.后来查了一下网上的资料,才知道,昨天我加入git的时候,为了看见 .git这个隐藏 ...

  10. Run-Time Check Failure #2 - Stack around the variable 'ucPriKey' was corrupt

    Run-Time    Check    Failure    #2        一般是栈被破坏,你的代码可能有缓冲区溢出一类的问题. Run-Time Check Failure #2 - Sta ...