在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)的更多相关文章

  1. iOS多线程系列(2)

    前面了iOS的NSThread方法来实现多线程,这篇就简单的讲讲NSOperation和NSOperationQueue. NSOperation是一个抽象类,定义一个要执行的任务.NSOperati ...

  2. iOS多线程系列(1)

    多线程这个概念的接触是蛮早的时候了,当时还是单核单CPU的时候,Thread这个概念已经出现了,当时比较流行的方案是时间片轮流,线程可以优先级抢占,但一次只能运行一个线程,实际上多线程是不能真正并行处 ...

  3. iOS开发系列--数据存取

    概览 在iOS开发中数据存储的方式可以归纳为两类:一类是存储为文件,另一类是存储到数据库.例如前面IOS开发系列-Objective-C之Foundation框架的文章中提到归档.plist文件存储, ...

  4. iOS开发系列--网络开发

    概览 大部分应用程序都或多或少会牵扯到网络开发,例如说新浪微博.微信等,这些应用本身可能采用iOS开发,但是所有的数据支撑都是基于后台网络服务器的.如今,网络编程越来越普遍,孤立的应用通常是没有生命力 ...

  5. iOS开发系列--并行开发其实很容易

    --多线程开发 概览 大家都知道,在开发过程中应该尽可能减少用户等待时间,让程序尽可能快的完成运算.可是无论是哪种语言开发的程序最终往往转换成汇编语言进而解释成机器码来执行.但是机器码是按顺序执行的, ...

  6. 【转】iOS开发系列--数据存取

    原文: http://www.cnblogs.com/kenshincui/p/4077833.html#SQLite 概览 在iOS开发中数据存储的方式可以归纳为两类:一类是存储为文件,另一类是存储 ...

  7. iOS多线程开发之离不开的GCD(上篇)

    一.GCD基本概念 GCD 全称Grand Central Dispatch(大中枢队列调度),是一套低层API,提供了⼀种新的方法来进⾏并发程序编写.从基本功能上讲,GCD有点像NSOperatio ...

  8. iOS开发系列

    因为最近面试了一些人,校招.初中级.高级.架构师,各种级别的,发现大家水平差异很大,有的高级的工程师很多问题都回答不上来,所以想梳理下iOS的知识点,写成一个系列,如果时间允许的话,会录制成视频放到网 ...

  9. iOS 多线程:『RunLoop』详尽总结

    1. RunLoop 简介 1.1 什么是 RunLoop? 可以理解为字面意思:Run 表示运行,Loop 表示循环.结合在一起就是运行的循环的意思.哈哈,我更愿意翻译为『跑圈』.直观理解就像是不停 ...

随机推荐

  1. Python之路第七天,基础(9)-面向对象(上)

    面向对象的编程思想 回想 我们所学过的编程方法: 面向过程:根据业务逻辑从上到下写堆叠代码. 函数式编程:将重复的代码封装到函数中,只需要写一遍,之后仅调用函数即可. 面向过程编程最易被初学者接受,其 ...

  2. Java GC机制和对象Finalize方法的一点总结

    GC是垃圾收集的意思(Garbage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超 ...

  3. Gradle方式构建Java多项目

    0: 安装IDEA:Linux 上只需下载IDEA的包 并且将路径配置在PATH全局变量中./etc/profile export PATH=$PATH:/opt/Software/IDEA/idea ...

  4. Repository仓储 UnitofWork

    Repository仓储 UnitofWork 目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 本章我们来创建仓储类Repository 并且引入 UnitOfWork 我对 ...

  5. 厂商自定义USB设备类概述

    USB协会将常用具有相同/相似功能的设备归为一类,并制定了相关的设备类规范,这样就能保障只要依照同样的规范标准,即使不同的厂商开发的USB设备也可以使用同样的驱动程序,而且操作系统中无须为每种设备提供 ...

  6. ACM计算几何题目推荐

    //第一期 计算几何题的特点与做题要领: 1.大部分不会很难,少部分题目思路很巧妙 2.做计算几何题目,模板很重要,模板必须高度可靠. 3.要注意代码的组织,因为计算几何的题目很容易上两百行代码,里面 ...

  7. 查看实时公网ip

    icanhazip.com 使您在任何地方知道你的公网IP地址 icanhazip.com 使你在任何地方知道你的公网IP地址 icanhazip.com 使你在任何地方知道你的公网IP地址 ican ...

  8. Mysql数据库启动

    1.改变数据库的访问ip: vim /etc/mysql/my.cnf 找到下面选项: bind-address            = 192.168.1.128 修改即可. 2.mysql启动命 ...

  9. UI设计师不可不知的安卓屏幕知识

    不少设计师和工程师都被安卓设备纷繁的屏幕搞得晕头转向,我既做UI设计,也做过一点安卓界面布局,刚好对这块内容比较熟悉,也曾在公司内部做过相关的讲座,在此,我将此部分知识重新梳理出来分享给大家! 1.了 ...

  10. HTML注释简介

    HTML注释简介   在编写HTML代码时,我们经常要在一些关键代码旁做一下注释,这样做的好处很多,比如:方便理解.方便查找或方便项目组里的其它程序员了解你的代码,而且可以方便以后你对自己代码进行修改 ...