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 表示循环.结合在一起就是运行的循环的意思.哈哈,我更愿意翻译为『跑圈』.直观理解就像是不停 ...
随机推荐
- Python信息采集器使用轻量级关系型数据库SQLite
1,引言Python自带一个轻量级的关系型数据库SQLite.这一数据库使用SQL语言.SQLite作为后端数据库,可以搭配Python建网站,或者为python网络爬虫存储数据.SQLite还在其它 ...
- Linux下vim文件未正常退出,修复文件
Linux下vim文件未正常退出,会产生一个 .文件名.swp的文件 ls -al ,rm 删掉.swp文件 之后就可以正常使用文件了
- ASCII、ANSI、GB2312、Unicode、UTF-8之间的关系
1.ASCII码: ASCII(American Standard Code for Information Interchange,美国信息互换标准代码)是基于拉丁字母的一套电脑编码系统.它主要用于 ...
- OSCHina技术导向:Java WEB企业门户平台Liferay
Liferay 是一个完整的门户解决方案,基于J2EE的应用,使用了EJB以及JMS等技术,前台界面部分使用Struts MVC 框架,基于XML的portlet配置文件可以自由地动态扩展,使用了We ...
- Vim应用
:q!不保存退出 :set number显示行数 :wq保存并退出 ==先输入100,再输入==.从这行开始向下100行,进行自动缩进对齐
- pomelo初探
最近发现了一个比较好玩的东西pomelo.地址:点击打开链接 这个东西是网易开发的一套基于node.js的高性能,分布式游戏服务器框架.这套框架不仅可以用来开发游戏服务器,也可用于开发高实时web应用 ...
- mvc3.0防止跨站点请求伪造(CSRF)攻击
众所周知,asp.net mvc程序在浏览器运行是产生标准的Html标签,包括浏览器要发送的关键数据等内容都在html内容里面.听起来不错,但是假如我们伪造类似的html内容,更改里面的关键数据,在浏 ...
- 从linux telnet到exchange邮件server来測试发送邮件
我们在Linux下,能够通过telnet邮件server的25port(一般smtp邮件server都是这个),来測试是否能发送邮件. 前提是先得把DNS配好,或者/etc/hosts文件制定好邮件s ...
- Asp.Net MVC4下设置W3P3(IIS)调试步骤
环境] VS 2012 IIS7.5 [问题] MVC项目在创建时和APS.NET不同,不能够选择服务器类型,不能够直接把项目创建到IIS上. 如果在项目中直接更改属性,更换调试服务器类型,会报错 ...
- 引言:Canvas绘图API快速入门
引言:Canvas绘图API快速入门 在接触HTML5的初学者包括我都在很多地方见到非常炫的一些页面,甚至好多学习HTML5的开发者都是冲着Web端的页游去的,那么HTML5那么绚丽的页面效果以及游戏 ...