`

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

阅读更多

原文地址:http://www.tanhao.me/pieces/356.html/

 

什么是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函数传入优先级来访问队列。
用户队列: 用户队列 (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_async 函数会立即返回, block会在后台异步执行。

当然,通常,任务完成时简单地NSLog个消息不是个事儿。在典型的Cocoa程序中,你很有可能希望在任务完成时更新界面,这就意味着需要在主线程中执行一些代码。你可以简单地完成这个任务——使用嵌套的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_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途径有几个好处:

平行计算: 注意在第二个版本的代码中, -setSomething:是怎么使用dispatch_async的。调用 -setSomething:会立即返回,然后这一大堆工作会在后台执行。如果updateSomethingCaches是一个很费时费力的任务,且调用者将要进行一项处理器高负荷任务,那么这样做会很棒。
安全: 使用GCD,我们就不可能意外写出具有不成对Lock的代码。在常规Lock代码中,我们很可能在解锁之前让代码返回了。使用GCD,队列通常持续运行,你必将归还控制权。
控制: 使用GCD我们可以挂起和恢复dispatch queue,而这是基于锁的方法所不能实现的。我们还可以将一个用户队列指向另一个dspatch queue,使得这个用户队列继承那个dispatch queue的属性。使用这种方法,队列的优先级可以被调整——通过将该队列指向一个不同的全局队列,若有必要的话,这个队列甚至可以被用来在主线程上执行代码。
集成: GCD的事件系统与dispatch queue相集成。对象需要使用的任何事件或者计时器都可以从该对象的队列中指向,使得这些句柄可以自动在该队列上执行,从而使得句柄可以与对象自动同步。

总结

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

该系列文章转载自:http://www.dreamingwish.com/

分享到:
评论

相关推荐

    GCD基本概念

    GCD(Grand Central Dispatch)是苹果公司为Mac OS X和iOS操作系统开发的一种基于C语言的低层次API。GCD提供了一种新的并发编程方法,通过将任务切分成多个单元并提交至工作队列中,使这些任务可以并发地或串行地...

    ios demo,dispatch_async,DISPATCH_QUEUE_CONCURRENT,多任务并发执行,自动创建多线程

    压缩包中的`test_gcd_thread_asynchronous_concurrent`可能是一个测试项目,包含了一些用于演示`dispatch_async`和`DISPATCH_QUEUE_CONCURRENT`使用的代码文件。在这个项目中,开发者可能可以看到如何创建并行队列、...

    并发编程之Operation Queue和GCD

    目前有三种类型的Dispatch Queue:串行队列(Serial dispatch queue)、并行队列(Concurrent dispatch queue)和主队列(Main dispatch queue)。 串行队列是一种只能处理一个任务的队列,可以由用户调用dispatch_...

    GCD实战一:使用串行队列实现简单的预加载 - 51CTO.COM1

    在iOS开发中,Grand Central Dispatch(GCD)是一种强大的多线程管理工具,它由Apple引入,用于简化并发编程。本文将重点讲解如何利用GCD中的串行队列实现简单的预加载策略。串行队列确保了队列中的任务按照添加的...

    GCD实战二:资源竞争 - 51CTO.COM1

    开发者可以通过`dispatch_queue_create()`创建一个串行队列,并使用`dispatch_async()`或`dispatch_sync()`来提交任务。 2. **全局并行队列**:GCD提供了一些预定义的全局队列,允许并发执行任务。但是,如果多个...

    gcdTest下载图片 dispatch_async

    标题中的“gcdTest下载图片 dispatch_async”涉及到两个主要的iOS编程概念:GCD(Grand Central Dispatch)和异步图像下载。GCD是苹果为多核处理器优化并发编程提供的一种技术,而dispatch_async函数是GCD中用于在...

    iOS GCD详解

    Dispatch Queues是GCD的基本概念,dispatch queue是一个对象,它可以接受任务,并将任务以先到先执行的顺序来执行。dispatch queue可以是并发的或串行的。GCD中有三种队列类型: 1. 主队列(main queue):与主线程...

    iOS开发:深入理解GCD 第二篇(dispatch_group、dispatch_barrier、基于线程安全的多读单写)1

    在提供的示例代码中,创建了一个并发队列(DISPATCH_QUEUE_CONCURRENT)和一个dispatch_group,然后将三个任务分别追加到组中。尽管任务的执行顺序是不确定的,但"done"的输出总是在所有任务完成之后,这展示了...

    iOS开发多线程-GCD介绍 - iOS知识库1

    3. GCD有两个核心概念:任务和队列。任务是执行什么操作,队列是用来存放任务的。 三、GCD的使用 1. 定制任务 2. 确定想做的操作,将任务添加到队列中,GCD会自动将队列中的任务取出,放到对应的线程中执行。 四、...

    iOS GCD 开发教程 完整版

    提供的"iOS Swift GCD 开发教程 HTML 版"是一个完整的教程,涵盖了GCD的基本概念、使用方法以及高级特性。通过学习这个教程,你可以深入了解如何在Swift项目中有效地使用GCD进行并发编程,提升应用性能。 总之,GCD...

    iOS GCD多核编程

    当向并发队列提交任务时,GCD会根据系统资源动态调整任务的执行,可能在一个核心上执行,也可能在多个核心上并发执行,这取决于任务的数量和系统的负载。 三、异步执行任务 在GCD中,可以使用`dispatch_async`函数...

    Cocoa多线程编程之 block 与 dispatch quene

    主要有两种类型的dispatch queue:串行队列(serial queue)和并行队列(concurrent queue)。 1. 串行队列:每个任务都会按照添加到队列的顺序依次执行,同一时间只有一个任务在运行。这确保了任务的执行顺序,...

    GCD是Apple提供的一套用于实现多线程编程的底层API.docx

    一、基本概念 全称:Grand Central Dispatch 类型:基于C语言的底层API 所属库:libdispatch 用途:为并发代码在iOS和macOS的多核硬件上执行提供支持 二、核心优势 自动管理线程:GCD自动管理线程的生命周期,包括...

    ios 开发多线程

    - 创建DispatchQueue:`dispatch_queue_create()`用于创建自定义队列,`dispatch_get_main_queue()`和`dispatch_get_global_queue()`获取主线程队列和全局并行队列。 - 异步/同步提交任务:`dispatch_async()`异步...

    GCD线程基础学习资源

    一、GCD的基本概念 1. 工作队列:GCD的核心是工作队列,它负责调度任务。工作队列分为串行队列和并行队列。 2. 串行队列:一个串行队列中的任务会按照添加的顺序依次执行,同一时间只有一个任务在运行。 3. 并行队列...

    GCD使用的相关方法

    GCD(Grand Central Dispatch)是Apple在其操作系统macOS和iOS中引入的一种并行编程技术,它简化了多核处理器和多线程编程。GCD基于队列和任务的概念,允许开发者高效地管理并发任务,提高应用程序的性能。下面将...

    GCD封装成面向对象

    在iOS和macOS开发中,Grand Central Dispatch(GCD)是一种强大的多线程技术,用于管理并发任务。GCD是Apple的系统级框架,它基于C语言实现,但通过面向对象的封装,我们可以让开发者以更加简洁、易懂的方式使用GCD...

    Constructing your own 地上爬梯好次 queues with GCD

    首先,我们需要理解GCD的基本概念。GCD是一个底层的任务调度库,由Apple开发,用于管理线程和任务执行。它提供了一种抽象的方式来处理并发,而不是直接操作线程。GCD管理一组工作线程,我们只需要提交任务到适当的...

    多线程GCD,NSThread,NSOperationQueue,详细解释Demo

    GCD的核心概念有队列(Dispatch Queue)和任务(Dispatch Work Item)。 1. **主队列(Main Queue)**:在主线程上执行任务,用于更新UI。 2. **并行队列(Concurrent Queue)**:可以在多个线程上并发地执行任务。...

Global site tag (gtag.js) - Google Analytics