iOS多线程系列(3)
在2011的WWDC上,苹果推出了GCD,从此多线程增加了一种新的方法。GCD要求运行在iOS4.0版本以上或者OS X10.6版本以上。GCD是Grand Central Dispatch的缩写,是一组用于实现并发编程的C接口。GCD是基于Objective-C的Block的特性开发的,基本的业务逻辑和NSOperation很像。都是添加一个任务到一个队列,由系统来负责线程的生成和调度。因为直接使用Block,所以使用起来很是方便,降低了多线程开发的门槛。
还是先看一下代码,和多线程系列(1)里面同一个例子,用GCD实现如下:
- (void)viewDidLoad
{
[super viewDidLoad]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self downloadImage:IMAGE_URL];
});
}
GCD的调用接口非常简单,就是将任务提交到Queue里面。
dispatch_async函数是异步非阻塞的,调用后会立刻返回,工作由系统在线程池中分配线程去执行。有异步的当然也有同步的,dispatch_sync就是同步的阻塞的API,会一直到添加的任务完成才会返回。
GCD实现多线程确实很简单,不需要了解多线程中的很多细节,而且效率也高。不过disaptch_queue有一些特殊的地方,实际使用中需要了解的多一些。dispatch_queue有串行运行和并行运行两种,顾名思义,串行运行就是任务顺序执行,完成一个然后执行下一个,每次只有一个任务在运行;并行运行就是各个任务可以同时运行,同时有多少任务可以并行是根据系统当时的负载决定的,这个开发者不用关心。
系统提供了3中类型的dispatch queue:
1. main queue
这实际上就是主线程的队列,所以很明显,这是一个串行的queue,所有加入main queue的任务都会发动主线程运行,所以加入任务时需要注意不要加入长时间运行的任务。
2. Global queue
我们实际开发中最常用的队列,是并发队列。并且有high、default、low三个优先级(每个优先级都对应一个独立的queue)。通过dispatch_get_global_queue这个API可以获得queue。
3. 自定义queue
dispatch queue是可以自己创建的,通过dispatch_queue_create这个API来创建,dispatch_queue_create(const char *label, dispatch_queue_attr attr)这个API的第一个参数是queue的名字,要求不能重复,所以很多时候和java一样,推荐用倒写的域名,第二个参数是建立的queue的类型。这里要指出,在iOS4.3之前,只能建立串行的queue,参数就是传递DISPATCH_QUEUE_SERIAL,iOS4.3之后可以建立并行的queue了,参数是DISPATCH_QUEUE_CONCURRENT。
看到create就会牵涉到内存的管理问题,GCD的内存管理同样是用引用计数的方式,不过并不纳入iOS的内存管理,所以是需要开发者手动管理的(无论是不是ARC)。
由于有着不同类型的队列,dispatch_async也可以嵌套使用,还是以同样的例子,我们也可以这样写:
- (void)viewDidLoad
{
[super viewDidLoad];
__block UIImage *_image; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:IMAGE_URL]];
_image = [[UIImage alloc] initWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = _image;
});
});
}
这样就在一段代码里面实现了所有的功能,包括后台下载,下载之后刷新UI,而且简单清晰。
还有一些常用的API介绍如下:
dispatch_get_current_queue()获取当前队列
dispatch_queue_get_label()获取队列的名字,如果队列没有名字,返回NULL
dispatch_set_target_queue()设定给定对象的目标队列
dispatch_main()会阻塞主线程等待主队列main queue中的Block执行结束。
有时我们会遇到运行一系列的任务,当任务全部结束后运行另一个特殊的任务这种场景。如果我们用dispatch_sync方法来串行运行所有的任务可以确定运行的先后顺序,但效率就会大大降低;但dispatch_async是异步非阻塞的,所以代码如下写是没用的,不能保证结束所有任务后那个特殊任务的运行时间点。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for(id obj in array)
dispatch_async(queue, ^{
[self doWork:obj];
});
[self doneWork];
针对这种情况,GCD提供了dispatch group,可以将一组任务集合在一起,等待这组任务完成后再继续,上面的场景,代码应该写成下面的样子:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for(id obj in array)
dispatch_group_async(group, queue, ^{
[self doWork:obj];
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
[self doneWork];
方法很简单,就是将并发的任务用dispatch_group_async异步添加到一个Group和全局队列中,dispatch_group_wait会等待这些工作完成后在返回。这样就实现了任务的顺序运行,不过dispatch_group_wait是会阻塞线程的,所以如果是主线程,这个API是不能调用的,那么我们该怎么办呢?
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for(id obj in array)
dispatch_group_async(group, queue, ^{
[self doWork:obj];
});
dispatch_group_notify(group, queue, ^{
[self doneWork];
});
dispatch_release(group);
答案还是很简单,换一个API,使用dispatch_group_notify这个方法即可。
有的时候我们要同步执行对数组元素的逐个操作,GCD提供了一个简单的dispatch_apply方法:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply([array count], queue, ^(size_t index){
[self doWork:obj:[array objectAtIndex:index]];
});
[self doneWork];
在使用dispatch_async方法提交并行的任务时,是无法确定任务的执行顺序的,但有时我们确实需要某些工作在某个工作完成之后执行,那么可以使用Dispatch Barrier接口来实现。
dispatch_async(queue, block1);
dispatch_async(queue, block2);
dispatch_barrier_async(queue, block3);
dispatch_async(queue, block4);
dispatch_async(queue, block5);
dispatch_barrier_async是异步的,调用后立刻返回。这样的写法会保证block1和block2并行执行完成后才会执行block3,完成后再会并行运行block4和block5。
请注意,这里的queue是一个并行队列,而且是自定义的那种。
作为苹果推出的多线程的神器,GCD的内容当然远远不止这些。不过通过介绍的最最常用的这些,我们已经可以管中窥豹了。GCD针对各种不同的需求考虑的很全面,并给出了相关的解决方案。开发者使用GCD应该说是很容易的,所以真正需要关心的就变成了任务怎么划分,怎么运行,是串行还是并行等等。
附上苹果的Grand Central Dispatch(GCD)Reference文档,需要深入了解的请参考。
iOS多线程系列(3)的更多相关文章
- iOS多线程系列(2)
前面了iOS的NSThread方法来实现多线程,这篇就简单的讲讲NSOperation和NSOperationQueue. NSOperation是一个抽象类,定义一个要执行的任务.NSOperati ...
- iOS多线程系列(1)
多线程这个概念的接触是蛮早的时候了,当时还是单核单CPU的时候,Thread这个概念已经出现了,当时比较流行的方案是时间片轮流,线程可以优先级抢占,但一次只能运行一个线程,实际上多线程是不能真正并行处 ...
- iOS开发系列--数据存取
概览 在iOS开发中数据存储的方式可以归纳为两类:一类是存储为文件,另一类是存储到数据库.例如前面IOS开发系列-Objective-C之Foundation框架的文章中提到归档.plist文件存储, ...
- iOS开发系列--网络开发
概览 大部分应用程序都或多或少会牵扯到网络开发,例如说新浪微博.微信等,这些应用本身可能采用iOS开发,但是所有的数据支撑都是基于后台网络服务器的.如今,网络编程越来越普遍,孤立的应用通常是没有生命力 ...
- iOS开发系列--并行开发其实很容易
--多线程开发 概览 大家都知道,在开发过程中应该尽可能减少用户等待时间,让程序尽可能快的完成运算.可是无论是哪种语言开发的程序最终往往转换成汇编语言进而解释成机器码来执行.但是机器码是按顺序执行的, ...
- 【转】iOS开发系列--数据存取
原文: http://www.cnblogs.com/kenshincui/p/4077833.html#SQLite 概览 在iOS开发中数据存储的方式可以归纳为两类:一类是存储为文件,另一类是存储 ...
- iOS多线程开发之离不开的GCD(上篇)
一.GCD基本概念 GCD 全称Grand Central Dispatch(大中枢队列调度),是一套低层API,提供了⼀种新的方法来进⾏并发程序编写.从基本功能上讲,GCD有点像NSOperatio ...
- iOS开发系列
因为最近面试了一些人,校招.初中级.高级.架构师,各种级别的,发现大家水平差异很大,有的高级的工程师很多问题都回答不上来,所以想梳理下iOS的知识点,写成一个系列,如果时间允许的话,会录制成视频放到网 ...
- iOS 多线程:『RunLoop』详尽总结
1. RunLoop 简介 1.1 什么是 RunLoop? 可以理解为字面意思:Run 表示运行,Loop 表示循环.结合在一起就是运行的循环的意思.哈哈,我更愿意翻译为『跑圈』.直观理解就像是不停 ...
随机推荐
- openstack安装记录(二)keystone安装
先决条件 在你配置 OpenStack 身份认证服务前,你必须创建一个数据库和管理员令牌. 完成下面的步骤以创建数据库: 用数据库连接客户端以 root 用户连接到数据库服务器: $ mysql -u ...
- a标签的click事件问题
easy知识点, a标签的click事件和href共存会在执行click事件后触发href里面的链接,如果把href设为空字符串:href="",则会使用当前页面的url作为跳转链 ...
- 通过Wmi实现Hyper-V远程管理(一)
最近公司需要做Hyper-V的远程管理,在现有产品基础上扩展对Hyper V的管理,实现远程开关机.远程开启虚拟机会话,其他内容可查看MSDN中有对Hyper-V的描述和相关实例代码. Wmi操作hy ...
- django models 类型整理 version:1.8.3
django models 类型整理 version:1.8.3 网上百度到的最上面的一篇已经是11年的了,django变化很大,现在把1.8.3版的models类型大致整理了下贴出来 普通键部分 F ...
- FMDB将对象放进数据库[二](使用runtime)
@import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...
- Nginx学习之一-第一个程序Hello World
本例子实现了一个简单的hello world程序.运行效果: 虚拟机Ubuntu中: win7中chrome浏览器: 一.config文件编写 Nginx提供了一种简单的方式将第三方的模块编译到Ngi ...
- 新建Android工程没有自动生成R.JAVA,应该先升级下ADT
前几天非常郁闷,本来计划在Android上做个小东西,结果打开Eclipse新建工程,发现居然没有R.JAVA! 反复测试很多次,均未成功,最后试着升级了下ADT,结果搞定,在这里记下,下次遇到这样的 ...
- Jquery基础之事件操作
事件是用户操作时页面或页面加载时引发的用来完成javascript和HTML之间的交互操作.常见的元素点击事件.鼠标事件.键盘输入事件等,较传Javascript 相比JQuery增加并扩展了基本的事 ...
- 【HDU】4092 Nice boat(多校第四场1006) ——线段树 懒惰标记
Nice boat Time Limit: 30000/15000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) To ...
- poj 3253 Fence Repair(模拟huffman树 + 优先队列)
题意:如果要切断一个长度为a的木条需要花费代价a, 问要切出要求的n个木条所需的最小代价. 思路:模拟huffman树,每次选取最小的两个数加入结果,再将这两个数的和加入队列. 注意priority_ ...