GCD简介三:Dispatch Sources

一、何为Dispatch Sources

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

我们到底讨论哪些事件类型?
下面是GCD 10.6.0版本支持的事件:
     Mach port send right state changes.
     Mach port receive right state changes.
     External process state change.
     File descriptor ready for read.
     File descriptor ready for write.
     Filesystem node event.
     POSIX signal.
     Custom timer.
     Custom event.
这是一堆很有用的东西,它支持所有kqueue所支持的事件(kqueue是什么?见http://en.wikipedia.org/wiki/Kqueue)以及mach(mach是什么?见http://en.wikipedia.org/wiki/Mach_(kernel))端口、内建计时器支持(这样我们就不用使用超时参数来创建自己的计时器)和用户事件。
 
二、用户事件

这些事件里面多数都可以从名字中看出含义,但是你可能想知道啥叫用户事件。简单地说,这种事件是由你调用dispatch_source_merge_data函数来向自己发出的信号。
这个名字对于一个发出事件信号的函数来说,太怪异了。这个名字的来由是GCD会在事件句柄被执行之前自动将多个事件进行联结。你可以将数据“拼接”至dispatch source中任意次,并且如果dispatch queue在这期间繁忙的话,GCD只会调用该句柄一次(不要觉得这样会有问题,看完下面的内容你就明白了)。

用户事件有两种: DISPATCH_SOURCE_TYPE_DATA_ADD 和 DISPATCH_SOURCE_TYPE_DATA_OR.用户事件源有个 unsigned long data属性,我们将一个 unsigned long传入 dispatch_source_merge_data。当使用 _ADD版本时,事件在联结时会把这些数字相加。当使用 _OR版本时,事件在联结时会把这些数字逻辑与运算。当事件句柄执行时,我们可以使用dispatch_source_get_data函数访问当前值,然后这个值会被重置为0。

让我假设一种情况。假设一些异步执行的代码会更新一个进度条。因为主线程只不过是GCD的另一个dispatch queue而已,所以我们可以将GUI更新工作push到主线程中。然而,这些事件可能会有一大堆,我们不想对GUI进行频繁而累赘的更新,理想的情况是当主线程繁忙时将所有的改变联结起来。
用dispatch source就完美了,使用DISPATCH_SOURCE_TYPE_DATA_ADD,我们可以将工作拼接起来,然后主线程可以知道从上一次处理完事件到现在一共发生了多少改变,然后将这一整段改变一次更新至进度条。

啥也不说了,上代码:
    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
    dispatch_source_set_event_handler(source, ^{
        [progressIndicator incrementBy:dispatch_source_get_data(source)];
    });
    dispatch_resume(source);

    dispatch_apply([array count], globalQueue, ^(size_t index) {
        // do some work on data at index
        dispatch_source_merge_data(source, 1);
    });
(对于这段代码,我很想说点什么,我第一次用dispatch source时,我纠结了很久很久,真让人崩溃:Dispatch source启动时默认状态是挂起的,我们创建完毕之后得主动恢复,否则事件不会被传递,也不会被执行)

假设你已经将进度条的min/max值设置好了,那么这段代码就完美了。数据会被并发处理。当每一段数据完成后,会通知dispatch source并将dispatch source data加1,这样我们就认为一个单元的工作完成了。事件句柄根据已完成的工作单元来更新进度条。若主线程比较空闲并且这些工作单元进行的比较慢,那么事件句柄会在每个工作单元完成的时候被调用,实时更新。如果主线程忙于其他工作,或者工作单元完成速度很快,那么完成事件会被联结起来,导致进度条只在主线程变得可用时才被更新,并且一次将积累的改变更新至GUI。

现在你可能会想,听起来倒是不错,但是要是我不想让事件被联结呢?有时候你可能想让每一次信号都会引起响应,什么后台的智能玩意儿统统不要。啊。。其实很简单的,把你的思想放到禁锢的框子之外就行了。如果你想让每一个信号都得到响应,那使用dispatch_async函数不就行了。实际上,使用的dispatch source而不使用dispatch_async的唯一原因就是利用联结的优势。

三、内建事件

上面就是怎样使用用户事件,那么内建事件呢?看看下面这个例子,用GCD读取标准输入:
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t stdinSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
                                                           STDIN_FILENO,
                                                           0,
                                                           globalQueue);
    dispatch_source_set_event_handler(stdinSource, ^{
        char buf[1024];
        int len = read(STDIN_FILENO, buf, sizeof(buf));
        if(len > 0)
            NSLog(@"Got data from stdin: %.*s", len, buf);
    });
    dispatch_resume(stdinSource);
 
简单的要死!因为我们使用的是全局队列,句柄自动在后台执行,与程序的其他部分并行,这意味着对这种情况的提速:事件进入程序时,程序正在处理其他事务。

这是标准的UNIX方式来处理事务的好处,不用去写loop。如果使用经典的 read调用,我们还得万分留神,因为返回的数据可能比请求的少,还得忍受无厘头的“errors”,比如 EINTR (终端系统调用)。使用GCD,我们啥都不用管,就从这些蛋疼的情况里解脱了。如果我们在文件描述符中留下了未读取的数据,GCD会再次调用我们的句柄。

对于标准输入,这没什么问题,但是对于其他文件描述符,我们必须考虑在完成读写之后怎样清除描述符。对于dispatch source还处于活跃状态时,我们决不能关闭描述符。如果另一个文件描述符被创建了(可能是另一个线程创建的)并且新的描述符刚好被分配了相同的数字,那么你的dispatch source可能会在不应该的时候突然进入读写状态。de这个bug可不是什么好玩的事儿。
适当的清除方式是使用 dispatch_source_set_cancel_handler,并传入一个block来关闭文件描述符。然后我们使用 dispatch_source_cancel来取消dispatch source,使得句柄被调用,然后文件描述符被关闭。

使用其他dispatch source类型也差不多。总的来说,你提供一个source(mach port、文件描述符、进程ID等等)的区分符来作为diapatch source的句柄。mask参数通常不会被使用,但是对于 DISPATCH_SOURCE_TYPE_PROC 来说mask指的是我们想要接受哪一种进程事件。然后我们提供一个句柄,然后恢复这个source(前面我加粗字体所说的,得先恢复),搞定。dispatch source也提供一个特定于source的data,我们使用 dispatch_source_get_data函数来访问它。例如,文件描述符会给出大致可用的字节数。进程source会给出上次调用之后发生的事件的mask。具体每种source给出的data的含义,看man page吧。

四、计时器

计时器事件稍有不同。它们不使用handle/mask参数,计时器事件使用另外一个函数 dispatch_source_set_timer 来配置计时器。这个函数使用三个参数来控制计时器触发:
 start参数控制计时器第一次触发的时刻。参数类型是 dispatch_time_t,这是一个opaque类型,我们不能直接操作它。我们得需要 dispatch_time 和  dispatch_walltime 函数来创建它们。另外,常量  DISPATCH_TIME_NOW 和 DISPATCH_TIME_FOREVER 通常很有用。
 
interval参数没什么好解释的。
 
leeway参数比较有意思。这个参数告诉系统我们需要计时器触发的精准程度。所有的计时器都不会保证100%精准,这个参数用来告诉系统你希望系统保证精准的努力程度。如果你希望一个计时器没五秒触发一次,并且越准越好,那么你传递0为参数。另外,如果是一个周期性任务,比如检查email,那么你会希望每十分钟检查一次,但是不用那么精准。所以你可以传入60,告诉系统60秒的误差是可接受的。

这样有什么意义呢?简单来说,就是降低资源消耗。如果系统可以让cpu休息足够长的时间,并在每次醒来的时候执行一个任务集合,而不是不断的醒来睡去以执行任务,那么系统会更高效。如果传入一个比较大的leeway给你的计时器,意味着你允许系统拖延你的计时器来将计时器任务与其他任务联合起来一起执行。

五、总结
现在你知道怎样使用GCD的dispatch source功能来监视文件描述符、计时器、联结的用户事件以及其他类似的行为。由于dispatch source完全与dispatch queue相集成,所以你可以使用任意的dispatch queue。你可以将一个dispatch source的句柄在主线程中执行、在全局队列中并发执行、或者在用户队列中串行执行(执行时会将程序的其他模块的运算考虑在内)。

转载自:http://www.dreamingwish.com/dream-2012/gcd介绍(三)-dispatch-sources.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/500136.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GCD简介四:挂起,目标指定,信号量

一、Dispatch Queue挂起dispatch queue可以被挂起和恢复。使用 dispatch_suspend函数来挂起,使用 dispatch_resume 函数来恢复。这两个函数的行为是如你所愿的。另外,这两个还是也可以用于dispatch source。一个要注意的地方是,dispatch que…

如何自己做网站发布到服务器上面,怎么做网站 四步教你搭建自己的网站

1、购买域名如果想要做一个自己的网站,购买一个自己喜欢的域名就是首先要做的!当你购买好自己的域名,就走出了搭建自己网站的第一步!2、购买空间/主机/服务器空间/主机/服务器,看着好像很复杂,其实很简单&a…

GCD简介五:补充

1.GCD在iOS4.0及以上可用。2.GCD中,主线程队列是串行的;全局队列是并行的,并由整个进程共享;用户自建队列,在iOS4.3以下,只能是串行,iOS4.3及以上,可以是并行的。3.dispatch_suspend…

ajax delete 传递参数,springMVC使用PUT、DELETE方法传递参数解决方案

在web.xml中新增过滤器HiddenHttpMethodFilterorg.springframework.web.filter.HiddenHttpMethodFilterHiddenHttpMethodFilter/*需要注意的是,只有context-type:application/x-www-form-urlencoded的请求才会被过滤。该过滤器的核心方法如下&#xff1a…

ARC简介

1、简介:ARC(Automatic Reference Counting),自动引用计数,需要XCode4.2和SDK5.0的支持。它是一个在编译期间工作的技术,编译器在编译的时候会根据变量的作用域为objective-c变量添加合适的retain、release、autorelease等函数。原…

微信验证码无法连接到服务器,微信无法连接到服务器1237解决方法

太多朋友在使用微信过程中出现微信无法连接到服务器1237的问题,出现这个问题的原因有很多种,接下来小编带大家找出问题原因,然后成功解决这个问题。以下是网友们成功解决此问题的方法,大家可以参考一下方法一:首先重启…

iOS后台任务

一、概览1.从iOS4.0开始,系统添加了多任务特征,允许应用在按下Home键之后,继续执行后台任务。大部分应用在进入后台之后就进入了挂起状态,只有那些为用户提供重要服务的App能够在进入后台之后继续运行一段时间。2.一般情况下&…

王者荣耀服务器维护bug,8月23日王者荣耀ios版更新一直显示维护是什么情况?更新出现bug 附处理方法...

相信很多喜爱玩王者荣耀的游戏玩家可以知道今天是王者荣耀星计划更新的日子,但是有很多苹果手机的小伙伴出现了一个问题,那就是无法进入游戏,甚至是更新十分的缓慢,在这里本文为您带来最新的解决方法!苹果手机微信区一…

服务器任务栏不显示程序,Win10任务栏不显示应用程序标签怎么办?

Win10任务栏不显示应用程序标签怎么办?执拗的人才会一条路走到黑,多个朋友多条路,多个方法多种选择。关于Win10任务栏不显示应用程序标签,一般是explorer进程出现故障导致,哦!忘记说了Win10系统下这东东改了个好听的名字&#xf…

Objective-C复制解析

一、为什么使用复制?1.C语言以及Objective-C语言总是按值传递的,按值传递意味着是隐式复制。在这里就分为两种情况:(1)如果是非对象类型,对传递过来的值进行更改,只会更改副本,对原始值没有影响&#xff0c…

udp协议的服务器是哪种类型,UDP协议

UDP协议1.UDP使用场景域名系统若是执行时间内,没有响应返回,证明包已丢失。音频或视频数据包的丢失只会造成通话或视频的干扰和不清楚,而不会像TCP协议那样造成通话的丢失或卡顿。2.UDP和TCP的类比UDP类似于邮局,数据的接受与否和…

归档和解档-Archiver

一、概念1.归档用于保存对象,包括被归档对象之间的各种相互关系或者依赖性。解档用于重建以前归档的对象与关系。在其它面向对象的语言中,一般使用术语"序列化"来描述归档解档模式。2.归档的对象通常存储为二进制数据,可以在内存或…

碧雪情天服务器地址源如何修改,稀有游戏《碧雪情天online》网络版王者归来一键服务端+客户端 支持转生系统和新图...

碧雪情天online王者归来版本介绍:1.增加新的职业套装;2.增加高川台、雪域、魔灵和天庭等新图;3.仙侠岭先知处添加角色转生功能;4.碣石村宝官添加装备签名和祝福卡兑换角色经验功能;5.增加宠物经验兑换角色经验功能;6.其它新内容和…

Objective-C单例

一、要点:1.当在应用程序中只需要有类的一个实例,并且可以被其它对象轻松的访问的时候,可以使用单例类。2.首先需要声明一个静态的类的实例变量,并初始化为nil。应该将该实例变量声明在类的实现文件中,表示变量仅在该实…

刚买的ubuntu服务器 为什么没有文件,新买的linux服务器怎么使用

新买的linux服务器怎么使用 内容精选换一换切换操作系统是为您的弹性云服务器重新切换一个系统盘。切换完成后弹性云服务器的系统盘ID会发生改变,并删除原有系统盘。如果弹性云服务器当前使用的操作系统不能满足业务需求(如软件要求的操作系统版本较高),…

iOS应用横竖屏切换

一、概述: 在iOS应用中,由UIViewController来控制屏幕翻转,根据需要随设备方向自动切换。在iOS6和之前的系统之间,控制方法发生了些变化。二、视图伸缩属性:1.UIView类的autoresizingMask属性,用来指定当它…

iOS简单动画实现方案

一、仿射变换--CGAffineTransform1.仿射变换,一种基于二维坐标的转换功能。CGAffineTransform数据结构代表一个二维坐标系的转换矩阵。2.通过CGAffineTransform数据结构,可以构造二维坐标系内的平移、伸缩、旋转矩阵。详情可以参看苹果开发文档中关于该数…

三维转换矩阵解释

一、图解 二、单位矩阵{1.0f,0.0f,0.0f,0.0f;0.0f,1.0f,0.0f,0.0f;0.0f,0.0f,1.0f,0.0f;0.0f,0.0f,0.0f,1.0f;}

Objective-C文件流操作

一、NSFileHandle1.它能够以流的方式访问基于文件、网络、管道、设备的数据,可以设置写入的位置指针。2.NSData、NSString写入文件的方法是直接覆盖写入,全部读出,当处理大数据的文件时,对内存会造成压力。而NSFileHandle能够以流…

CoreAnimation编程指南(简介)

一、核心动画编程介绍1.本文档介绍了在使用核心动画时所涉及的基本概念。核心动画的是Objective – C的框架,它通过简单的动画编程接口来提供一套高性能的动画引擎。2.你应该阅读此文档来理解Cocoa应用程序核心动画工作的机制。 阅读此文档的前提是你已经掌握了Obje…