1 简介

1.1 功能

         Grand Central Dispatch(GCD)技术让任务并行排队执行,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务。任务可以是一个函数(function)或者是一个blockGCD的底层依然是用线程实现,不过这样可以让程序员不用关注实现的细节。

GCD中的队列称为dispatch queue,它可以保证先进来的任务先得到执行通过它能够大大简化多线程编程。工程师只要将要执行的任务(执行代码块)放入队列中,GCD将会为需要执行的任务创建thread,从而放入dispatch queue中,当将任务添加到队列立即安排开始执行。

  • 任务:其是并发程序的执行单位;
  • 队列:其是并发程序的管理单位。

1.2 第一个程序

1) Object-C 语言

 1 int main(int argc, const char * argv[]) {
 2     //创建队列
 3     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 4     //添加block任务
 5     dispatch_async(queue, ^{
 6         printf("hello world\n");
 7     });
 8     sleep(2); //若不休眠2秒,主线程就会退出,导致dispatch queue线程也退出。
 9     return 0;
10 }
11 输出:
12     hello world

2) Swift 语言

1 override func viewDidLoad() {
2         super.viewDidLoad()
3         let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)    //创建队列 
4         dispatch_async(queue){    //添加block任务
5             print("hello world\n");
6         }
7  }
8 输出:
9     hello world

2 设计与概念

2.1 Queue类型

Dispatch queue的实现机制是基于C实现的,并且GCD自动为用户提供了一些dispatch queue,当然也可以自定义一些queue。其中queue类型只有三种:Serial、Concurrent和Main dispatch queue。

表 1 dispatch queue类型

Type

Description

Serial(串行)

又称为private dispatch queues,同一时刻只执行一个任务,并按添加到serial的顺序执行。当创建多个Serial queue时,虽然它们各自是同步执行的,但Serial queue与Serial queue之间是并发执行的。Serial queue通常用于同步访问特定的资源或数据。

Concurrent(并行)

又称为global dispatch queue,同一时刻可执行多个任务,任务开始执行的顺序按添加的顺序执行,但是执行完成的顺序是随机的,同时可以创建执行的任务数量依赖系统条件。

Main dispatch queue(主队列)

它是全局可用的serial queue,它是在应用程序主线程上执行任务的

2.2 Queue相关技术

除了dispatch queue外,GCD还提供了一些技术来辅助queue管理代码。

表 2 相关技术

Type

Description

Dispatch groups

Dispatch group是用来监控一组block对象的完成情况,可以根据需要对block对象进行异步或同步操作。

Dispatch semaphores

Dispatch semaphores非常类似传统的信号量,但效率更高。

Dispatch sources

Dispatch source是为了响应指定系统事件,而产生的消息。当一个事件发生,那么dispatch source将提交你的异步任务代码到指定的dispatch queue线程中。

3 创建队列

       dispatch queue的队列有serial、concurrent和main三种,如下分别介绍如何获得这 三种队列。

3.1 并行队列

     由于系统已经为每个应用程序创建了四个不同优先级的Concurrent dispatch queue,用户不需要创建Concurrent dispatch queue,只需通过dispatch_get_global_queue 函数获得就可以,其声明如下:

dispatch_queue_t dispatch_get_global_queue(long identifier,long flags);

  • identifier:该属性用于设置queue优先级,有DISPATCH_QUEUE_PRIORITY_HIGH, DISPATCH_QUEUE_PRIORITY_DEFAULT,DISPATCH_QUEUE_PRIORITY_LOW,DISPATCH_QUEUE_PRIORITY_BACKGROUND;
  • flags:该属性是保留值,目前只需设置为0。

Ps:

       虽然dispatch queue是reference-counted对象,但是由于是全局对象,所以不需要手动进行retain 和 release。

3.2 串行队列

不像Concurrent dispatch queue有全局的队列,Serial Dispatch Queue需要用户手动进行创建和管理。其中创建的函数是dispatch_queue_create,其声明如下:

dispatch_queue_t dispatch_queue_create(const char *label dispatch_queue_attr_t attr);

  • label :描述queue的名字,是唯一的;
  • attr:是保留值,目前设置为NULL即可。

3.3 主队列

除了需要用户自定义serial dispatch queue外,系统还为用户创建了一个serial queue并将其绑定到应有程序的main thread中。用户可以通过dispatch_get_main_queue直接获得。其声明如下:

dispatch_queue_t dispatch_get_main_queue(void);

3.4 queue context

所有的dispatch 对象(包括dispatch queue)都允许指定一个自定义结构的context(上下文)对象,可以通过dispatch_set_context和dispatch_get_context函数,设置和获得这个context对象,系统不会使用这个对象,其只是负责传递这个对象,所有用户需要自己进行创建和释放。

对于dispatch queue,可以在context结构中存放一个指向Objective-C或标量结构的指针,从而可以在queue代码中使用这个指针,最后可以在queue的清理函数中释放这个指针,具体例子可以参考1.3.5节。

3.5 finalizer function

在创建了一个serial dispatch queue后,可以手动设置一个清理函数(finalizer function)给queue,从而当queue退出时能够调用该清理函数。其中dispatch queue也是一个Objective-C对象,所以其也拥有一个引用计数,但dispatch queue的引用计数为0时,会被系统回收,在回收之前会调用指定的清理函数。可以通过dispatch_set_finalizer_f函数来设置清理函数。

如下的例子为配置了一个queue context对象,并在定制的清理函数中释放这个queue context指针。

 1 void myFinalizerFunction(void *context)
 2 {
 3 MyDataContext* theData = (MyDataContext*)context;
 4 myCleanUpDataContextFunction(theData);  // Clean up the contents of the structure
 5 free(theData); // Now release the structure itself.
 6 }
 7 dispatch_queue_t createMyQueue()
 8 {
 9 MyDataContext* data = (MyDataContext*) malloc(sizeof(MyDataContext));
10 myInitializeDataContextFunction(data);
11 // Create the queue and set the context data.
12 dispatch_queue_t serialQueue = dispatch_queue_create("com.example.CriticalTaskQueue", NULL);
13 if (serialQueue)
14 {
15 dispatch_set_context(serialQueue, data);
16 dispatch_set_finalizer_f(serialQueue, &myFinalizerFunction);
17 }
18 return serialQueue;
19 }

4 添加任务

      为了执行任务,用户必须将任务添加(dispatch)到合适的dispatch queue中。可以将任务以异步或同步的方式执行,同时可以单独将任务添加到queue,或者是将多个任务组成group。一旦任务被添加到queue中,queue将尽可能完成任务的执行。

4.1 添加single任务

添加single任务是指将任务单独添加到queue中,可以知道queue中的任务是按添加的顺序开始执行,但无法确定任务何时被执行。GCD有两种方式将任务添加到queue中:同步异步

1) 异步:dispatch_async函数

void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

     该函数将block或函数添加到queue中,且添加后立即返回,不需要等待block或函数被执行完成。其中由queue的类型来决定同一个dispatch queue是串行还是并行执行,而不同的dispatch queue则都是并行执行的。

2) 同步:dispatch_syn函数

void dispatch_sync( dispatch_queue_t queue, dispatch_block_t block);

      该函数将block或函数添加到dispatch queue后,不会立即返回,必须等待block执行完成才能返回。串行或并行同样受queue的类型决定。

4.2 添加任务到主线程

由于dispatch queue本身是线程安全的,而UIKit则非线程安全类型。所以从后台线程向任何GUI对象发送消息都是不可能的,既在dispatch queue中的任务无法对GUI的对象进行修改。然而有一种解决办法:可以通过dispatch_get_main_queue函数获得主线程队列,接着将需要访问的任务发布到主线程队列中。如swift实现:

1 let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
2 dispatch_async(queue){
3     dispatch_async(dispatch_get_main_queue())
4       {
5         self.TextView.text = resultSummary;//访问UI中的TextView控件
6     }
7 }

5 其它技术

5.1 等待group任务完成

         dispatch group是队列的一种组合,其将多个dispatch queue组合为一个组,通过group使得某个block等待其它线程完成后,该block才得以执行。其中相关的函数有:

  • dispatch_group_create

通过该函数创建一个dispatch group,

  • dispatch_group_async(group, queue, block)

其语义与dispatch_async类似,都是将任务添加到queue中。该函数不同的是将queue的任务组合到group中。

  • dispatch_group_notify(group, queue, block)

该函数是指等待group中的任务都完成后,才执行block任务。其中这里的queue与dispatch_group_async的queue是不相关的,可以不同。

如object-C 语言的例子:

 1     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 2     dispatch_group_t group = dispatch_group_create();
 3     dispatch_group_async(group, queue, ^{
 4         [NSThread sleepForTimeInterval:1];
 5         NSLog(@"group1");
 6     });
 7     dispatch_group_async(group, queue, ^{
 8         [NSThread sleepForTimeInterval:2];
 9         NSLog(@"group2");
10     });
11     dispatch_group_async(group, queue, ^{
12         [NSThread sleepForTimeInterval:3];
13         NSLog(@"group3");
14     });
15     dispatch_group_notify(group, dispatch_get_main_queue(), ^{
16         NSLog(@"updateUi");
17     });
18     dispatch_release(group);

5.2 任务同步操作

dispatch_barrier_async是一种同步操作,在其前面的任务执行结束后它才执行,而且其后面的任务等它执行完成之后才会执行。

如object-C实现:

 1 dispatch_queue_t queue = dispatch_queue_create("gcdtest", DISPATCH_QUEUE_CONCURRENT);
 2 dispatch_async(queue, ^{
 3         [NSThread sleepForTimeInterval:2];
 4         NSLog(@"dispatch_async1");
 5     });
 6 dispatch_async(queue, ^{
 7         [NSThread sleepForTimeInterval:4];
 8         NSLog(@"dispatch_async2");
 9     });
10 dispatch_barrier_async(queue, ^{
11         NSLog(@"dispatch_barrier_async");
12         [NSThread sleepForTimeInterval:4];
13     });
14 dispatch_async(queue, ^{
15         [NSThread sleepForTimeInterval:1];
16         NSLog(@"dispatch_async3");
17 });
18 输出为:
19      dispatch_async1
20      dispatch_async2
21      dispatch_barrier_async
22      dispatch_async3

5.3 循环迭代

通过dispatch_apply可以执行某段代码块n次,从而来替换循环(while或for)操作。

如一般情况下的循环语句为:

1 for (i = 0; i < count; i++) {
2    printf("%u\n",i);
3 }

通过dispatch queue操作为:

1 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2 dispatch_apply(count, queue, ^(size_t i) {
3    printf("%u\n",i);
4 });

6 参考文献

  1. Apple:Concurrency Programming Guide
  2. Apple:Grand Central Dispatch (GCD) Reference

iOS 并行编程:GCD Dispatch Queues的更多相关文章

  1. iOS 并行编程:NSOperation Queues

    1 简介 1.1 功能        Operation Queue也是IOS的一种并行编程技术,类似Dispatch Queue可以帮助用户管理多线程.但是Operation Queue将任务封装在 ...

  2. iOS 并行编程:GCD Dispatch Sources

    1 简介 dispatch source是一种用于处理事件的数据类型,这些被处理的事件为操作系统中的底层级别.Grand Central Dispatch(GCD)支持如下的dispatch sour ...

  3. IOS并发编程GCD

    iOS有三种多线程编程的技术 (一)NSThread  (二)Cocoa NSOperation (三)GCD(全称:Grand Central Dispatch) 这三种编程方式从上到下,抽象度层次 ...

  4. iOS 并行编程:Thread

    1 创建线程 1.1 NSThread       使用 NSThread 来创建线程有两个可以使用的方法: 1) 使用 detachNewThreadSelector:toTarget:withOb ...

  5. IOS开发 多线程GCD

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

  6. IOS中的多核并发编程GCD

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

  7. iOS多线程编程技术之NSThread、Cocoa NSOperation、GCD

    原文出处: 容芳志的博客 简介iOS有三种多线程编程的技术,分别是:(一)NSThread(二)Cocoa NSOperation(三)GCD(全称:Grand Central Dispatch) 这 ...

  8. iOS并发编程笔记【转】

    线程 使用Instruments的CPU strategy view查看代码如何在多核CPU中执行.创建线程可以使用POSIX 线程API,或者NSThread(封装POSIX 线程API).下面是并 ...

  9. ios 中的 GCD

    摘自:http://www.cocoachina.com/swift/20150129/11057.html libdispatch是Apple所提供的在IOS和OS X上进行并发编程的库,而GCD正 ...

随机推荐

  1. Android中使用proguardgui混淆jar包

    本文章的前提条件是,读者已经掌握了正确导出jar包的技能. 1.拷贝Android项目中"proguard.cfg"文件到你指定的位置,并改名为"proguard.pro ...

  2. 基于LAMP平台的网站架构(或Web系统架构)

    1.网站架构的前提(或者说需求) 我们公司是一电子商务的网站,因为线下家具建材项目的推广需求,从而有了我们公司的这个线上网站,在这里我贴一张公司的网站架构图. 总体来说网站规模不是太大,注册人数在15 ...

  3. keytool 错误: java.io.FileNotFoundException: 拒绝访问

    keytool 错误: java.io.FileNotFoundException: 拒绝访问 打开命令行,切换到D:\AndroidStudioProjects\MyApplication\app目 ...

  4. c#桥接模式(bridge结构模式)

    桥接模式(bridge结构模式)c#简单例子 在前面的玩家中每增加一个行为,就必须在每个玩家中都增加,通过桥接模式将行为提取出来了,减少变化 ? 1 2 3 4 5 6 7 8 9 10 11 12 ...

  5. 找出Java进程中大量消耗CPU

    原文:https://github.com/oldratlee/useful-shells useful-shells 把平时有用的手动操作做成脚本,这样可以便捷的使用. show-busy-java ...

  6. spring--处理器拦截器详解——跟着开涛学SpringMVC

    5.1.处理器拦截器简介 Spring Web MVC的处理器拦截器(如无特殊说明,下文所说的拦截器即处理器拦截器) 类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理. ...

  7. [Stephen]Android的adb无法启动

    1.程序中运行收入cmd,打开dos命令窗口,在窗口中依次运行abd kill-server和  adb start-server 尝试重启adb服务 2.如果依然启动失败  dos命令窗口中键入  ...

  8. [洛谷1580]yyy loves Easter_Egg I

    题目背景 Soha的出题效率着实让人大吃一惊.OI,数学,化学的题目都出好了,物理的题还没有一道.于是,Huntfire,absi2011,lanlan对soha进行轮番炸,准备炸到soha出来,不料 ...

  9. 一个老站长对HTML5认识

    新的一年开始,各个领域都在悄悄发生着一些变化.在移动互联网领域也在发生着很多变化,其中HTML5的技术在多年的实践和国内巨头公司的大力推广下,HTML5的webapp几乎可以和原生态的app分庭抗礼了 ...

  10. CURL超时处理

    一般会设置一个超时时间1S,就是说如果php那边在1S内没有返回给urlserver的话就忽略掉该请求,及不阻塞等待返回了,直接处理下面的操作. 现在php那边有时候会卡,这样一卡就无法再1S内返回消 ...