什么是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
2
3
4
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self goDoSomethingLongAndInvolved];
        NSLog(@"Done doing something long and involved");
});

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

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

1
2
3
4
5
6
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简单办到:

1
2
3
4
5
6
7
8
__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;

访问代码会像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (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_sync将共享数据的访问代码封装起来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (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.dreamingwish.com/dream-2012/of-of-of-of-gcd-introduced-1-basic-concepts-in-and-the-dispatch-queue.html

转载于:https://www.cnblogs.com/liushanshan/archive/2013/02/05/2893024.html

GCD介绍(一): 基本概念和Dispatch Queue (转载)相关推荐

  1. GCD简介一:基本概念和Dispatch Queue

    一.什么是GCD? Grand Central Dispatch或者GCD,是一套低层API,提供了一种新的方法来进行并发程序编写.从基本功能上讲,GCD有点像NSOperationQueue,他们都 ...

  2. GCD介绍(一): 基本概念和Dispatch Queue

    什么是GCD? Grand Central Dispatch或者GCD,是一套低层API,提供了一种新的方法来进行并发程序编写.从基本功能上讲,GCD有点像NSOperationQueue,他们都允许 ...

  3. GCD介绍(三): Dispatch Sources

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

  4. IOS并发编程指南:Dispatch Queue任务执行与Dispatch Source

    导读: 本文为读<Concurrency Programming Guide>笔记第三篇,在对OS X和iOS应用开发中实现任务异步执行的技术.注意事项.Operation与Dispatc ...

  5. Blocks与Dispatch Queue的使用

    2019独角兽企业重金招聘Python工程师标准>>> block是什么 block是一个C level的语法以及运行时的一个特性,和标准C中的函数(函数指针)类似.用于回调函数的地 ...

  6. 并发编程指南(二)Dispatch Queue

    Dispatch Queues Grand Central Dispatch(GCD)调度队列是执行任务的强大工具. 通过调度队列,您可以相对于调用者异步或同步地执行任意代码块. 您可以使用调度队列执 ...

  7. 服务编排--Conductor 文档翻译 (介绍与基本概念)

    介绍 Conductor 优点 为什么不进行点对点编排? 基本概念 工作流定义 任务定义 系统任务 工人任务 工作流任务的生命周期 元数据定义 任务定义 重试逻辑 超时政策 工作流定义 工作流程中的任 ...

  8. 学习笔记Flink(一)—— Flink简介(介绍、基本概念、应用场景)

    一.Flink介绍 Apache Flink 是一个分布式流批一体化的开源平台.Flink 的核心是一个提供数据分发.通信以及自动容错的流计算引擎.Flink 在流计算之上构建批处理,并且原生的支持迭 ...

  9. C#多线程之旅(1)——介绍和基本概念

    阅读目录 一.多线程介绍 二.Join 和Sleep 三.线程怎样工作 四.线程和进程 五.线程的使用和误用 一.多线程介绍 C#通过多线程支持并行执行的代码.一个线程是一个独立执行的路径,可以同时与 ...

最新文章

  1. python 把列表或者元组转成集合
  2. git提交代码到github时出现everything up-to-date,但是代码没有上传成功
  3. go语言中的匿名函数
  4. 存储安全与数据恢复基础手册-服务器篇
  5. SmartGWT入门,提供出色的GWT界面
  6. 论文浅尝 - WWW2020 | 生成多跳推理问题以改善机器阅读理解能力
  7. 校园计算机网络系统,校园计算机网络系统
  8. Mac OSX使用隐藏文件夹
  9. 我的2015plan
  10. 数学分析教程(科大)——1.3笔记+习题
  11. 新买的电脑 Windows10/Windows11即将过期怎么办
  12. 大学英语精读第三版(第四册)复习笔记——文章内容摘要
  13. 关于网络拓扑图,你想知道的都在这
  14. 陈年再创业:B2C必须标准化 VANCL只做男装
  15. 给普通人的Python——第四章
  16. 兔子maya骨骼绑定_玛雅Maya模型怎么绑定骨骼? 玛雅Maya骨骼绑定教程
  17. Nyko推出平板手柄 为运行在Tegra3上的游戏特别打造
  18. 云班课计算机第二阶段测试卷,云班课测试自动答案
  19. 嵌入式 linux 蓝牙 C开发
  20. 软件测试面试常见的题目(内含答案)

热门文章

  1. 并查集 - 交换字符串中的元素
  2. P1020 导弹拦截(最长不上升序列+二分)
  3. 标准库类型string的基本功能和使用
  4. 实现库函数strcpy
  5. pytorch 之 torch.bmm()函数
  6. php 离线 gis,在 Web 页面中使用离线地图
  7. 软件测试功能测试都有哪一些,【软件测试基础】功能测试常用方法都有哪些
  8. Linux内存page,【原创】(十四)Linux内存管理之page fault处理
  9. excel sample
  10. Clojure 学习