GCD教程(四):完结
完结篇,原帖地址:http://www.dreamingwish.com/dream-2012/gcd-four-the-the-odds-and-ends.html
Dispatch Queue挂起
dispatch queue可以被挂起和恢复。使用 dispatch_suspend函数来挂起,使用 dispatch_resume 函数来恢复。这两个函数的行为是如你所愿的。另外,这两个还是也可以用于dispatch source。
一个要注意的地方是,dispatch queue的挂起是block粒度的。换句话说,挂起一个queue并不会将当前正在执行的block挂起。它会允许当前执行的block执行完毕,然后后续的block不再会被执行,直至queue被恢复。
还有一个注意点:从man页上得来的:如果你挂起了一个queue或者source,那么销毁它之前,必须先对其进行恢复。
Dispatch Queue目标指定
所有的用户队列都有一个目标队列概念。从本质上讲,一个用户队列实际上是不执行任何任务的,但是它会将任务传递给它的目标队列来执行。通常,目标队列是默认优先级的全局队列。
用户队列的目标队列可以用函数 dispatch_set_target_queue来修改。我们可以将任意dispatch queue传递给这个函数,甚至可以是另一个用户队列,只要别构成循环就行。这个函数可以用来设定用户队列的优先级。比如我们可以将用户队列的目标队列设定为低优先级的全局队列,那么我们的用户队列中的任务都会以低优先级执行。高优先级也是一样道理。
有一个用途,是将用户队列的目标定为main queue。这会导致所有提交到该用户队列的block在主线程中执行。这样做来替代直接在主线程中执行代码的好处在于,我们的用户队列可以单独地被挂起和恢复,还可以被重定目标至一个全局队列,然后所有的block会变成在全局队列上执行(只要你确保你的代码离开主线程不会有问题)。
还有一个用途,是将一个用户队列的目标队列指定为另一个用户队列。这样做可以强制多个队列相互协调地串行执行,这样足以构建一组队列,通过挂起和暂停那个目标队列,我们可以挂起和暂停整个组。想象这样一个程序:它扫描一组目录并且加载目录中的内容。为了避免磁盘竞争,我们要确定在同一个物理磁盘上同时只有一个文件加载任务在执行。而希望可以同时从不同的物理磁盘上读取多个文件。要实现这个,我们要做的就是创建一个dispatch queue结构,该结构为磁盘结构的镜像。
首先,我们会扫描系统并找到各个磁盘,为每个磁盘创建一个用户队列。然后扫描文件系统,并为每个文件系统创建一个用户队列,将这些用户队列的目标队列指向合适的磁盘用户队列。最后,每个目录扫描器有自己的队列,其目标队列指向目录所在的文件系统的队列。目录扫描器枚举自己的目录并为每个文件向自己的队列提交一个block。由于整个系统的建立方式,就使得每个物理磁盘被串行访问,而多个物理磁盘被并行访问。除了队列初始化过程,我们根本不需要手动干预什么东西。
信号量
dispatch的信号量是像其他的信号量一样的,如果你熟悉其他多线程系统中的信号量,那么这一节的东西再好理解不过了。
信号量是一个整形值并且具有一个初始计数值,并且支持两个操作:信号通知和等待。当一个信号量被信号通知,其计数会被增加。当一个线程在一个信号量上等待时,线程会被阻塞(如果有必要的话),直至计数器大于零,然后线程会减少这个计数。
我们使用函数 dispatch_semaphore_create 来创建dispatch信号量,使用函数 dispatch_semaphore_signal 来信号通知,使用函数dispatch_semaphore_wait 来等待。这些函数的man页有两个很好的例子,展示了怎样使用信号量来同步任务和有限资源访问控制。
单次初始化
GCD还提供单词初始化支持,这个与pthread中的函数 pthread_once 很相似。GCD提供的方式的优点在于它使用block而非函数指针,这就允许更自然的代码方式:
这个特性的主要用途是惰性单例初始化或者其他的线程安全数据共享。典型的单例初始化技术看起来像这样(线程安全的):
- + (id)sharedWhatever
- {
- static Whatever *whatever = nil;
- @synchronized([Whatever class])
- {
- if(!whatever)
- whatever = [[Whatever alloc] init];
- }
- return whatever;
- }
这挺好的,但是代价比较昂贵;每次调用 +sharedWhatever 函数都会付出取锁的代价,即使这个锁只需要进行一次。确实有更风骚的方式来实现这个,使用类似双向锁或者是原子操作的东西,但是这样挺难弄而且容易出错。
使用GCD,我们可以这样重写上面的方法,使用函数 dispatch_once:
- + (id)sharedWhatever
- {
- static dispatch_once_t pred;
- static Whatever *whatever = nil;
- dispatch_once(&pred, ^{
- whatever = [[Whatever alloc] init];
- });
- return whatever;
- }
这个稍微比 @synchronized方法简单些,并且GCD确保以更快的方式完成这些检测,它保证block中的代码在任何线程通过 dispatch_once 调用之前被执行,但它不会强制每次调用这个函数都让代码进行同步控制。实际上,如果你去看这个函数所在的头文件,你会发现目前它的实现其实是一个宏,进行了内联的初始化测试,这意味着通常情况下,你不用付出函数调用的负载代价,并且会有更少的同步控制负载。
结论
这一章,我们介绍了dispatch queue的挂起、恢复和目标重定,以及这些功能的一些用途。另外,我们还介绍了如何使用dispatch 信号量和单次初始化功能。到此,我已经完成了GCD如何运作以及如何使用的介绍。
GCD教程(四):完结的更多相关文章
- ASP.NET 5系列教程(七)完结篇-解读代码
在本文中,我们将一起查看TodoController 类代码. [Route] 属性定义了Controller的URL 模板: [Route("api/[controller]") ...
- canvas基础简单易懂教程(完结,多图)
目录 Canvas学习 一. Canvas概述 1.1 Hello world 1.2 Canvas的像素化 1.3 Canvas的动画思想 1.4 面向对象思维实现canvas动画 二.Canvas ...
- CRL快速开发框架系列教程四(删除数据)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- 手把手教从零开始在GitHub上使用Hexo搭建博客教程(四)-使用Travis自动部署Hexo(2)
前言 前面一篇文章介绍了Travis自动部署Hexo的常规使用教程,也是个人比较推荐的方法. 前文最后也提到了在Windows系统中可能会有一些小问题,为了在Windows系统中也可以实现使用Trav ...
- C#微信公众号开发系列教程四(接收普通消息)
微信公众号开发系列教程一(调试环境部署) 微信公众号开发系列教程一(调试环境部署续:vs远程调试) C#微信公众号开发系列教程二(新手接入指南) C#微信公众号开发系列教程三(消息体签名及加解密) C ...
- 无废话ExtJs 入门教程四[表单:FormPanel]
无废话ExtJs 入门教程四[表单:FormPanel] extjs技术交流,欢迎加群(201926085) 继上一节内容,我们在窗体里加了个表单.如下所示代码区的第28行位置,items:form. ...
- TFS(Team Foundation Server)敏捷使用教程(四):工作项跟踪(1)
工作项跟踪(1) 可跟踪性是软件过程的重要能力,TFS主要是以工作项来实现过程的可跟踪性.曾有人问:"你们实际项目里的工作项是怎么样的?能不能让我们看看?"我也一直很好奇别的公司T ...
- Android Studio系列教程四--Gradle基础
Android Studio系列教程四--Gradle基础 2014 年 12 月 18 日 DevTools 本文为个人原创,欢迎转载,但请务必在明显位置注明出处!http://stormzhang ...
- Laravel教程 四:数据库和Eloquent
Laravel教程 四:数据库和Eloquent 此文章为原创文章,未经同意,禁止转载. Eloquent Database 上一篇写了一些Laravel Blade的基本用法和给视图传递变量的几种方 ...
- NGUI系列教程四(自定义Atlas,Font)
今天我们来看一下怎么自定义NGUIAtlas,制作属于自己风格的UI.第一部分:自定义 Atlas1 . 首先我们要准备一些图标素材,也就是我们的UI素材,将其导入到unity工程中.2. 全选我们需 ...
随机推荐
- 丑数<数学技巧>
题意:丑数就是质因子只有2,3,5 ,7,的数,另外1也是丑数.求第n(1=<n<=5842)个丑数,n=0,结束. 思路:.3.5或者7的结果(1除外).那么,现在最主要的问题是如何排序 ...
- Block 朴实理解
转载自:http://www.cnblogs.com/lzz900201/archive/2013/04/17/3025340.html block是个什么玩意儿 Block是Apple Inc.为C ...
- oracle中nvl函数
最近在修改项目中一个统计的bug,统计出的钱数不对,因为不是自己开发的模块,经过分析流程找到了统计的sql. sum(f_msmoney)+sum(f_fkmoney) as total, 上面这段是 ...
- UIScrollView 和 UIPageControl
UIScrollView [滚动视图]非常重要 UIScrollView是滚动视图,是其它带有滚动功能视图的父类, 本身不显示或者只显示背景,主要负责子视图的滚动和翻页. 一.常用属性 1.基本方法 ...
- SpringMVC强大的数据绑定
6.6.2.@RequestParam绑定单个请求参数值 @RequestParam用于将请求参数区数据映射到功能处理方法的参数上. public String requestparam1(@Requ ...
- 《accelerated c++》第九章---设计类
本章简单介绍了类的设计,但是包含了一些好的设计原则. 1一些通用的设计原则 (1)一个函数是否设计成成员函数的原则:如果这个函数改变对象的状态,那么这个函数就应该成为这个对象的成员. (2)对于内置类 ...
- Restful based service 的跨域调用
1.关于跨域, w3c的官方文档:https://www.w3.org/TR/cors/ 2.有时间再整理吧. <html> <head> <script src=&qu ...
- Bundle display name 与 Bundle name 的区别
Bundle display name 与 Bundle name 的区别是什么?"Bundle Name" and "Bundle display name" ...
- LintCode 面试题 旋转字符串
1.题目描述 题目链接:http://www.lintcode.com/zh-cn/problem/rotate-string/ 给定一个字符串和一个偏移量,根据偏移量旋转字符串(从左向右旋转) 2. ...
- 把View转化成Image
+ (UIImage *) imageWithView:(UIView *)view { UIGraphicsBeginImageContextWithOptions(view.bounds.size ...