目录

  一、简介

  二、dispatch Queue - 队列

  三、dispatch Groups - 组

  四、dispatch Semaphores - 信号量

  五、dispatch Barriers - 障碍

  六、dispatch sources - 系统源

  七、dispatch I/O - I/O

  八、总结

一、简介

  GCD 的全称是 Grand Centre Dispatch 是一个强大的任务编程管理工具。通过GCD你可以同步或者异步地执行block、function。

二、dispatch Queue - 队列

  queue是指先进先出的列表,是blocks的执行环境。这个环境可以是单线程的也可以使多线程这个取决于你创建的queue类型。如果是serial queue(串行队列)队列的话,queue内部是以单线程运行。被添加进去的blocks,按照它们被添加进去的顺序串行地执行。

  如果是concurrent queue(并行队列),queue的内部线程个数由被添加的block执行的任务和当前系统有关。一般来说线程是会共用的,即是当一个block用完一个线程后,这个线程不会立马销毁会留着备用。这样就减少了创建线程的系统消耗。

  我们下面看看怎么用queue相关的API

  1. 创建queue

    有两个创建queue的方法

    一个是通过dispatch_queue_create手动创建queue,这个方法可以创建串行queue、并行queue

  函数定义:  

dispatch_queue_t dispatch_queue_create( const char *label dispatch_queue_attr_t attr);
参数 label 是指这个queue名字
参数 attr 可以指出需要创建的是什么类型的queue这里有两个值可选
DISPATCH_QUEUE_SERIAL ( NULL ) 串行queue
DISPATCH_QUEUE_CONCURRENT 并行queue 创建一个串行queue
dispatch_queue_t serial         = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);

   另一个是通过 dispatch_get_global_queue 获取系统默认的queue,这个方法返回queue是并行的

dispatch_queue_t dispatch_get_global_queue( long identifier, unsigned long flags);

参数 identifier  用来指明queue的运行的优先级,可以取下值:

  DISPATCH_QUEUE_PRIORITY_HIGH        高优先级
  DISPATCH_QUEUE_PRIORITY_DEFAULT      默认优先级
  DISPATCH_QUEUE_PRIORITY_LOW             低优先级
  DISPATCH_QUEUE_PRIORITY_BACKGROUND     后台

参数 flags       保留

    另外还可以通过 dispatch_get_main_queue 来获取mian queue。mian queue 是执行在主线程上的一个queue,它与消息循环交错的运行。

    通过调用 dispatch_get_current_queue 可以返回当前的queue。在一个被提交的block的内部调用 dispatch_get_current_queue 函数返回的是运行这个block的queue,在block之外调用返回的是main queue。      

  2. 把block添加到queue运行

    GCD也提供也支持把一个function添加到queue 的api,这个api的特点是多了后缀_f,比如的dispatch_async/dispatch_async_f

    一个block只有被提交到queue后才能被执行,一旦block被提交到queue就无法取消,而且我们是无法得知block执行的状态。所以在一些提交任务后需要取消的场景我们可以用NSOperation。

异步执行block
dispatch_queue_t serial = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
dispatch_async(serial, ^{
NSLog(@"from queue");
}); dispatch_async 异步的把一个block提交到queue,立即返回
dispatch_sync  同步的把一个block提交到queue,阻塞当前线程直到block执行完成 void dispatch_after( dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
dispatch_after 在指定的时间到达时异步的提交block到queue执行。
when 参数是指延迟时间,是一个dispatch time 类型,可以通过dispatch_time、dispatch_walltime这两个函数生成
void dispatch_once( dispatch_once_t *predicate, dispatch_block_t block);
dispatch_once 在app的生命周期里只执行一次,常用于创建单例

  3.void dispatch_set_target_queue( dispatch_object_t object, dispatch_queue_t queue);这个函数可以设置dispatch object的目标queue,dispatch object 可以使dispatch queue、dispatch source 、Dispatch I/O channels。目标queue就是dispatch object的finalizer(dispatch source)调用者,修改dispatch object的target queue会修改dispatch object如下的行为:

  当被修改的dispatch object 为 Dispatch queue时,被修改的queue的优先级与target queue的优先级一致。如果修改一个serial queue 的 target queue为另一个serial queue 。为了下面方便描述,我们把被修改的queue叫做queueA,把target queue叫做queueB。那么在set target queue之后,先把一个blockA异步的提交到queueA,再把blockB异步的提交到queueB,这时候blockB会先于blockA执行。文字描述可能有点不清晰,我们直接来看代码

   dispatch_queue_t serial1         = dispatch_queue_create("serial1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serial2 = dispatch_queue_create("serial2", DISPATCH_QUEUE_SERIAL);
dispatch_async(serial1, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"blockA");
});
dispatch_async(serial2, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"blockA");
});
dispatch_set_target_queue(serial1, serial2);
dispatch_async(serial1, ^{
NSLog(@"setted target : blockA ");
});
dispatch_async(serial2, ^{
NSLog(@"setted target : blockB");
}); 输出:

2017-01-06 21:02:32.268 GCDTest[7379:1744101] blockA
2017-01-06 21:02:32.268 GCDTest[7379:1744094] blockA
2017-01-06 21:02:32.269 GCDTest[7379:1744094] setted target : blockB
2017-01-06 21:02:32.269 GCDTest[7379:1744094] setted target : blockA

  至于为什么是这个结果我现在还不清楚,我猜测应该是提交到queueA的block,等待它的target queue -> queueB的正在排队的办block都提交后,才把blockA提交到target block -> queueB。

  当被修改的dispatch object 为 Dispatch source时,由新设置的target queue 来响应 Dispatch source的各个handle。

  4.void dispatch_main( void)  这个函数的作用是暂停一下主线程,等待所有被提交到main queue的任务执行完成。一般的通过 UIApplicationMain (iOS), NSApplicationMain (OS X), or CFRunLoopRun 创建的程序是禁止调用这个函数的,所以这个函数我们了解一下即可。

  看代码:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"main queue 1");
});
dispatch_main(); // 如果不加这句代码的话,上面提交的block是不会执行的 return ;
}

  5.上面提到dispatch_after函数,它的第一个参数需要一个dispatch_time_t类型,由dispatch_time、dispatch_walltime这两个函数生成(也可以取DISPATCH_TIME_NOW/DISPATCH_TIME_FOREVER这两个宏,第一个指现在马上,第二个指永远不)。现在我们来学习一下这两个函数

  首先是dispatch_time_t dispatch_time( dispatch_time_t when, int64_t delta); dispatch_time函数需要输入两个参数

  when 参数是指定一个开始值。一般取DISPATCH_TIME_NOW,意思是说从现在开始吧。

  delta 参数的单位是纳秒,

  dispatch_time 函数的作用就是返回一个相距 when 时间的 delta 个纳秒的时间间隔。比如一个延迟5秒的定时任务可以这样写:

// 从现在开始,5s后开始提交block到mian queue
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * );
dispatch_after(when, dispatch_get_main_queue(), ^{
NSLog(@"do something.");
});

  dispatch_walltime函数同dispatch_time函数的功能一样的,只是两者的第一个参数类型不一样 ,前者参考的时间是 wall clock,后者参考时间四default clock。至于default clock 与 wall clock 的区别,可以自行了解。如果可以用一个不那么严谨的解释来描述的话,就如先描述:dispatch_time返回的是相距一个时间间隔(此时此刻)多少纳秒的时间间隔,dispatch_walltime返回的是一个相距特定日期时间(2017-01-08 22:30)多少纳秒的时间间隔。

  下面给出使用dispatch_walltime的一个例子:

    NSDate * date = [NSDate date];
NSLog(@"date:%@",date); // 2017-01-08 22:30:00
NSTimeInterval interval = date.timeIntervalSince1970;
struct timespec time;
time.tv_nsec = NSEC_PER_SEC * interval;
time.tv_sec = ;
dispatch_time_t when = dispatch_walltime(&time,NSEC_PER_SEC*);
   // when 等于 从2017-01-8 22:30:00开始5s钟后,(2017-01-08 22:30:05 )
dispatch_after(when, dispatch_get_main_queue(), ^{
NSLog(@"do something.");
});     

三、dispatch Groups - 组

  通过dispatch groups你可以同步一组blocks,你不需要管blocks执行顺序,在所有被提交到groups的blocks都执行完成后,你会得到通知。整个dispatch_groups 技术涉及3个概念,我们先理一下:

  .block:执行任务的代码块,这个不用多说大家都知道

  .queue:实际运行block代码的线程,上一节已经谈过了

  .qroup:指把一些blocks,人工的组合在一起让它们成为一个组。

  一句话总结dispatch groups技术:

    创建一个group,并把一些blocks(需要指定执行queue)添加进来,等到所有的blocks都执行完后发通知到group。

  我们直接来看代码:  

    dispatch_queue_t serial     = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t concurrent = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, serial, ^{
NSLog(@"serial block");
});
dispatch_group_async(group, concurrent, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"concurrent blockA");
});
dispatch_group_async(group, concurrent, ^{
NSLog(@"concurrent blockB");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"group complete");
});
结果输出:
-- ::56.137 GCDTest[:] serial block
-- ::56.137 GCDTest[:] concurrent blockB
-- ::57.142 GCDTest[:] concurrent blockA
-- ::57.142 GCDTest[:] group complete

  上面例子中有两个api

  void dispatch_group_async( dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block); 异步的把一个block关联到一个group

  void dispatch_group_notify( dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);把一个complete block提交给group,当group的所有关联blocks都执行完成后,会执行你提交的complete block。如果group中没有关联的block的,会立即执行complete block。

  还有一个同步等待group执行完成通知的API : long dispatch_group_wait( dispatch_group_t group, dispatch_time_t timeout);

  dispatch_group_wait会一直阻塞着,直到group所有关联的blocks都执行完成或者第二个参数提供的超时时间已经超时。

  我们还可以通过这两个下面api来手动管理group的通知

  dispatch_group_enter( dispatch_group_t group);   // count 自增 1 (初始化为0)
  dispatch_group_leave( dispatch_group_t group);  // count 自减 1 (当count 为0时发complete 通知)

  我们直接看代码

    dispatch_queue_t concurrent1 = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t concurrent2 = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(concurrent1, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"concurrent1 block ");
dispatch_async(concurrent2, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"concurrent2 block ");
dispatch_group_leave(group);
});
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"group complete");
}); // 输出:
// 2017-01-09 10:26:31.752 GCDTest[10009:1858381] concurrent1 block
// 2017-01-09 10:26:32.756 GCDTest[10009:1858381] concurrent2 block
// 2017-01-09 10:26:32.756 GCDTest[10009:1858042] group complete

四、dispatch Semaphores - 信号量

  这个比较简单了,就是一个生产者消费者模式。

  首先创建一个信号信号量并指定一个初始化值:

    dispatch_semaphore_t dispatch_semaphore_create( long value);

  当生产出一个产品时,我们增加一下信号量,下面这个函数返回值是当期有多少个可用的产品

    long dispatch_semaphore_signal( dispatch_semaphore_t dsema);

  当我们需要产品的时候,调用一下wait函数,如果返回0说明有产品可用,非0则相反

    long dispatch_semaphore_wait( dispatch_semaphore_t dsema, dispatch_time_t timeout);

  直接上代码:    

// 生产者
dispatch_queue_t producer = dispatch_queue_create("producer", DISPATCH_QUEUE_CONCURRENT);
// 消费者
dispatch_queue_t client = dispatch_queue_create("client", DISPATCH_QUEUE_CONCURRENT);
// 信号量
dispatch_semaphore_t carFactory = dispatch_semaphore_create();
dispatch_async(producer, ^{
for (int i=; i < ; i++) {
[NSThread sleepForTimeInterval:i];
NSLog(@"[%d]output a car",i+);
dispatch_semaphore_signal(carFactory);
}
});
dispatch_async(client, ^{
while () {
long ret = dispatch_semaphore_wait(carFactory, DISPATCH_TIME_NOW); if (ret == ) {
NSLog(@"get a car");
}
}
});
/*
输出
2017-01-09 11:30:54.754 GCDTest[17921:1948479] [1]output a car
2017-01-09 11:30:54.755 GCDTest[17921:1948486] get a car
2017-01-09 11:30:55.759 GCDTest[17921:1948479] [2]output a car
2017-01-09 11:30:55.760 GCDTest[17921:1948486] get a car
2017-01-09 11:30:57.764 GCDTest[17921:1948479] [3]output a car
2017-01-09 11:30:57.765 GCDTest[17921:1948486] get a car
2017-01-09 11:31:00.767 GCDTest[17921:1948479] [4]output a car
2017-01-09 11:31:00.767 GCDTest[17921:1948486] get a car
2017-01-09 11:31:04.770 GCDTest[17921:1948479] [5]output a car
2017-01-09 11:31:04.770 GCDTest[17921:1948486] get a car
*/

五、dispatch Barriers - 障碍

  通过 dispatch_barrier 允许在并行queue中提交一个barrier block,假设我们把提交动作发生的这个时间点记做A点。完成提交barrier block动作后,会一直等待A点前提交的blocks都执行完毕才去执行barrier block,当barrier block被执行完毕后就开始按照concurrent queue既有的规制运行A点后提交的blocks。有两个提交barrier block的API,一个异步的,一个同步的。直接看代码

dispatch_queue_t concurrent = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrent, ^{
NSLog(@"block A");
});
dispatch_async(concurrent, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"block B");
});
// 异步提交
dispatch_barrier_async(concurrent, ^{
NSLog(@"barrier block ");
});
// 同步提交
dispatch_barrier_sync(concurrent, ^{
NSLog(@"barrier block");
});
dispatch_async(concurrent, ^{
NSLog(@"block C");
});
// 结果
-- ::17.203 GCDTest[:] block A
-- ::19.206 GCDTest[:] block B
-- ::19.207 GCDTest[:] barrier block
-- ::19.208 GCDTest[:] block C

  值得注意的是,只能把barrier block提交到自己通过dispatch_queue_crate创建的concurrent queue,如果提交到serial queue、global queue的话,barrier block不会起作用,与dispatch_sync、dispatch_async 无异

六、dispatch sources - 系统源

  通过dispatch source 可以监控系统的一些事件,当这些事件发生时会把你的block提交到queue执行。包括下列事件

  定时器事件

  描述符(文件)事件

  信号量事件

  进程事件

  在这里我只谈谈定时器怎么用的,其他事件在ios doc都有sample code;

  dispatch source 有一套api可以用,我先来个概念快照

    . dispatch_source_t dispatch_source_create( dispatch_source_type_t type, uintptr_t handle, unsigned long mask, dispatch_queue_t queue) 创建一个dispatch source

    . void dispatch_source_cancel( dispatch_source_t source);  取消一个dispatch source

    . void dispatch_source_set_event_handler( dispatch_source_t source, dispatch_block_t handler); 设置event handle block

    . void dispatch_source_set_cancel_handler( dispatch_source_t source, dispatch_block_t cancel_handler); 设置dispatch source 被取消的 handle block

    . void dispatch_source_set_timer( dispatch_source_t source, dispatch_time_t start, uint64_t interval, uint64_t leeway); // 给dispatch source设置一个定时器。

  看过api快照后,你应该大概知道怎么用GCD创建一个定时器了 :先创建一个dispatch source ,然后给它设置 event、cancel handler 的block 和 设置timer属性,最后调用dispatch_resume执行dispatch source就行了。我们在代码里面详细说明  

    __block int count = ;
/*
* dispatch_source_t dispatch_source_create( dispatch_source_type_t type, uintptr_t handle, unsigned long mask, dispatch_queue_t queue)
* 第一个参数指明这是一个timer,当然还可以指定其他类型
* 第二个参数是一个系统资源的句柄,比如文件句柄
* 第三个参数为flag
* 第四个参数是handle block被提交到的queue
*/
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, , , dispatch_get_main_queue());
if (timer) {
// 设置 timer
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, * NSEC_PER_SEC, );
// 设置 event handler
dispatch_source_set_event_handler(timer, ^{
NSLog(@"timer fire");
if (count++ == ) {
// 取消dispatch source
dispatch_source_cancel(timer);
}
});
// 设置 event handler
dispatch_source_set_cancel_handler(timer, ^{
NSLog(@"time cancel.");
});
// 启动 dispatch source
// 因为create后还需要配置一些行为,所以需要手动resume dispatch source
dispatch_resume(timer);
// 注意:
// 这里需要保存一下timer到类变量,不然timer是局部变量运行到函数尾部是,这个定时器也就没了
self.timer = timer;
}

七、dispatch I/O - I/O

     

八、总结

  最后再来看看dispatch_object的管理:

    //dispatch_object 对象的内存管理,用ARC技术的话,这两个函数就没有用了
void dispatch_release( dispatch_object_t object);
void dispatch_retain( dispatch_object_t object);
// 注意
void dispatch_resume( dispatch_object_t object); // counting 减1 等于0就回复
void dispatch_suspend( dispatch_object_t object); // counting 加1 大于0就挂起 // 可以给dispatch_object设置一个context用于在各个任务之间共享
void * dispatch_get_context( dispatch_object_t object); // 获取 context
void dispatch_set_context( dispatch_object_t object, void *context); // 设置 context // 可以给dispatch_object设置一个执行完成的回调函数
void dispatch_set_finalizer_f( dispatch_object_t object, dispatch_function_t finalizer);













GCD 开发详情的更多相关文章

  1. 玩转iOS开发:iOS中的GCD开发(三)

    上一章, 我们了解到了GCD里的一些队列和任务的知识, 也实践了一下, 同时我们也对主队列的一些小情况了解了一下, 比如上一章讲到的卡线程的问题, 如果没有看的朋友可以去看看玩转iOS开发:iOS中的 ...

  2. iOS 定时器开发详情

    目录 概述 NSTimer performSelector GCD timer CADisplayLink 一.概述 在平时的开发任务中,定时器是我们常用的技术.这一节我们来学习iOS怎么使用定时器. ...

  3. GCD 开发

    一.简介 GCD 的全称是 Grand Centre Dispatch 是一个强大的任务编程管理工具.通过GCD你可以同步或者异步地执行block.function. 二.dispatch Queue ...

  4. objective-c runtime 开发详情

    目录 概述 对象与类的实质 id与class 继承关系与isa 总结 C函数创建一个OC类 OC类与runtime NSObjectProtocol NSObject NSProxy 一.概述 Obj ...

  5. KVO 开发详情

    目录 概念 应用KVO的3个步骤 关联属性的KVO 手动管理KVO通知 一.概念 KVO全称是 Key-Value Observing ,是OC的一种消息发送机制.这个机制是指:假设将B对象注册为A对 ...

  6. KVC 开发详情

    目录 概述 KVC基本技术 KVC访问函数 KVC搜索顺序 KVC集合操作 一.概述 kvc全名是Key-value coding,kvc是一种通过字符串间接的访问oc对象的属性的一种技术. 一个oc ...

  7. swift开发多线程篇 - 多线程基础

    swift开发多线程篇 - 多线程基础 iOS 的三种多线程技术 (1)NSThread  使用NSThread对象建立一个线程非常方便 但是!要使用NSThread管理多个线程非常困难,不推荐使用 ...

  8. iOS开发——多线程OC篇&多线程详解

    多线程详解 前面介绍了多线程的各种方式及其使用,这里补一点关于多线程的概念及相关技巧与使用,相信前面不懂的地方看了这里之后你就对多线程基本上没有什么问题了! 1——首先ios开发多线程中必须了解的概念 ...

  9. 还在用GCD?来看看NSOperation吧

    在iOS开发中,谈到多线程,大家第一时间想到的一定是GCD.GCD固然是一套强大的多线程解决方案,能够解决绝大多数的多线程问题,但是他易于上手难于精通且到处是坑的特点也注定了想熟练使用它有一定的难度. ...

随机推荐

  1. 配置伪静态(URL重写)

    本篇借鉴了很多文章,这里做个记录. 有时我们的导航栏出现xx.aspx?id=x&name=xx 等等这样,会显得不好看,我们可以利用伪静态来美化我们的导航栏,伪静态的形式可以自己定义,本质还 ...

  2. Knockout 事件传递参数的方法

    在Knockout中直接使用函数传递参数是不行的,会导致函数在初始化时就被调用. 要实现参数的传递,有2种方法: 1.方法一:使用函数包裹 <div data-bind="event: ...

  3. angular2中一种换肤实现方案

    思路:整体思路是准备多套不同主题的css样式.在anguar项目启动时,首先加载的index.html中先引入一套默认的样式.当我们页面有动作时再切换css.  可以通过url传参触发,也可以通过bu ...

  4. 谈谈对bug的一点想法,说说做好日志记录的重要性

    说起程序猿,总绕不开的一个话题就是bug,估计每个程序猿听到某某测试跑过来一脸淫笑的告诉你这个功能有个bug的时候,总有种恨不得掐死他的想法.其实程序猿跟bug的关系,感觉有点像父亲和儿子的关系,自己 ...

  5. 子查询,用户管理,pymysql使用

    当我们的一条记录 分散不同的表中时,就需要进行多表查询例如 一对一 一对多 多对多 1.笛卡尔积查询 意思就是将两个表中的所有数据 全部关联在一起例如A表有两条 B表有三条 一共有6条会产生大量的错误 ...

  6. linux-shell——04

    mv 移动文件或者目录 格式:mv [选项]      源文件/目录     目标文件/目录 注:若移动目标位置与源位置相同(当前下操作),则此操作相当于重命名(改名) ex: [root@local ...

  7. 关于SSM框架项目中jsp页面EL表达式使用的一些疑问(一)

    问题 ssm框架整合中,jsp页面中EL表达式所引用的对象“page”可以在controller中使用mav.addObject(“page”,pag )进行添加,如果省略mav.addObject( ...

  8. Spark机器学习之推荐引擎

    一. 最小二乘法建立模型 关于最小二乘法矩阵分解,我们可以参阅: 一.矩阵分解模型. 用户对物品的打分行为可以表示成一个评分矩阵A(m*n),表示m个用户对n各物品的打分情况.如下图所示: 其中,A( ...

  9. 课后题2.87&2.86

    课后题2.86&2.87 单纯就是想加点分第十章的题目都被做过了就做下第二章的,正好复习一下前面学的知识,第二章给我剩下的题目也不多了,我就挑了这个题目. 2.86 考虑一个基于IEEE浮点格 ...

  10. STM8 输出比较极性

    PWM输出的信号极性有两个选项决定,一个选项是PWM模式,另一个选项是输出极性控制位. 请看定时器的框图,PWM模式的选择决定OC1REF的极性, 例如当选择PWM1模式时,OC1REF信号是&quo ...