文档章节

iOS多线程编程之三——GCD的应用

珲少
 珲少
发布于 2015/05/21 15:52
字数 2056
阅读 344
收藏 4

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

iOS多线程编程之三——GCD的应用

一、引言

在软件开发中使用多线程可以大大的提升用户体验度,增加工作效率。iOS系统中提供了多种分线程编程的方法,在前两篇博客都有提及:

NSThread类进行多线程编程:http://my.oschina.net/u/2340880/blog/416524

NSOperation进行多线程操作编程:http://my.oschina.net/u/2340880/blog/416782

上两个进行多线程编程的机制都是封装于Object-C的类与方法。这篇博客将讨论的Grand Central Dispatch(GCD)机制,则是基于C语言的,相比上面两种机制,GCD更加高效,并且线程有系统管理,会自动运用多核运算。因为这些优势,GCD是apple推荐我们使用的多线程解决方案。

二、GCD的调度机制

GCD机制中一个很重要的概念是调度队列,我们对线程的操作实际上是由调度队列完成的。我们只需要将要执行的任务添加到合适的调度队列中即可。

1、调度队列的类型

调度队列有三种类型:

(1)主队列

其中的任务在主线程中执行,因为其会阻塞主线程,所以这是一个串行的队列。可以通过dispatch_get_main_queue()方法得到。

(2)全局并行队列

队列中任务的执行方式是严格按照先进先出的模式进行了。如果是串行的队列,则当一个任务结束后,才会开启另一个任务,如果是并行队列,则任务的开启顺序是和添加顺序一致的。系统为iOS应用自动创建了四个全局共享的并发队列。使用如下函数获得:

 

dispatch_get_global_queue(long identifier, unsigned long flags);

其中第一个参数是这个队列的id,系统的四个全局队列默认的优先级不同,这个参数可填的定义如下:

#define DISPATCH_QUEUE_PRIORITY_HIGH 2//优先级最高的全局队列
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0//优先级中等的全局队列
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)//优先级低的全局队列
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN//后台的全局队列 优先级最低

这个函数的第二个参数,按照官方文档的说法是有待未来使用,现在我们都填0即可。

(3)自定义队列

上面的两种队列都是系统为我们创建好的,我们只需要获取到他们,将任务添加即可。当然,我们可可以创建我们自己的队列,包括串行的和并行的。使用如下方法创建:

dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);

其中,第一个参数是这个队列的名字,第二个参数决定创建的是串行的还是并行的队列。填写DISPATCH_QUEUE_SERIAL或者NULL创建串行队列,填写DISPATCH_QUEUE_CONCURRENT创建并行队列。

2、添加任务到队列中

使用dispatch_sync(dispatch_queue_t queue, dispatch_block_t block)函数或者dispatch_async(dispatch_queue_t queue, dispatch_block_t block)函数来同步或者异步的执行任务,示例如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        NSLog(@"%@:1",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"%@:2",[NSThread currentThread]);
    });
}

打印结果如下:

可以看出第一个任务在主线程中执行,第二个在分线程中执行。

三、队列调度机制的更多技巧

通过上面的演示,我们已经可以运用队列进行多线程的执行任务,但是GCD的强大之处远远不止如此。

1、使用队列组

如果有这样三个任务,A与B是没有关系的,他们可以并行执行,C必须在A,B结束之后才能执行,当然,实现这样的逻辑并不困难,使用KVO就可以实现,但是使用队列组处理这样的逻辑,代码会更加清晰简单。

可以使用dispatch_group_create()创建一个队列组,使用如下函数将队列添加到队列组中:

void dispatch_group_async(dispatch_group_t group,
	dispatch_queue_t queue,
	dispatch_block_t block);

队列组中的队列是异步执行的,示例如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    //创建一个队列组
    dispatch_group_t group=dispatch_group_create();
    创建一个异步队列
    dispatch_queue_t queue=dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_async(group, queue, ^{
        for (int i=0; i<10; i++) {
            NSLog(@"%@:%d",[NSThread currentThread],i);
        }
    });
    dispatch_group_async(group, queue, ^{
        for (int i=0; i<10; i++) {
            NSLog(@"%@:%d",[NSThread currentThread],i);
        }
    });
    //阻塞线程直到队列任务完成
    dispatch_group_wait(group,DISPATCH_TIME_FOREVER);
    for (int i=0; i<10; i++) {
        NSLog(@"over:%d",i);
    }
}

打印出来的信息如下:

可以看出,队列中的任务是异步执行的,并且等待队列组中队列任务全部执行后才执行后面的任务。这样的做法在实际应用中我们很少使用,通常我们会把后续的任务在放在异步中执行,做法如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    //创建一个队列组
    dispatch_group_t group=dispatch_group_create();
    //创建一个队列
    dispatch_queue_t queue=dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
    //添加队列任务到队列组
    dispatch_group_async(group, queue, ^{
        for (int i=0; i<10; i++) {
            NSLog(@"%@:%d",[NSThread currentThread],i);
        }
    });
    dispatch_group_async(group, queue, ^{
        for (int i=0; i<10; i++) {
            NSLog(@"%@:%d",[NSThread currentThread],i);
        }
    });
    //队列组任务执行完后执行的任务
    dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (int i=0; i<10; i++) {
            NSLog(@"over:%d",i);
        }
    });
    for (int i=0; i<10; i++) {
         NSLog(@"Finish:%d",i);
    }
   
}

打印信息如下:

可以看出GCD的强大了吧,复杂的任务逻辑关系因为GCD变得十分清晰简单。

2、循环机制

一开始我们就提到,GCD相比NSOperation的优势在于多核心的应用,更深得挖掘出了硬件的性能。GCD在多核方面的一个明显的特点就是循环机制。

 dispatch_apply(10, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t i) {
        NSLog(@"%@:%zu",[NSThread currentThread],i);
    });

打印结果如下:

可以看出,程序的运行效率又会高许多。

3、消息传递机制

 

dispatch_source_t类型的对象可以用来传递和接受某个消息,然后执行block方法,示例如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    //创建一个数据对象,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, ^{
        NSLog(@"%lu",dispatch_source_get_data(source));
    });
    //启动
    dispatch_resume(source);
    //设置数据
    dispatch_source_merge_data(source, 1);
    //这步执行完之后会执行打印方法
}

 

4、发送和等待信号

GCD中还有一个重要的概念是信号量。它的用法法消息的传递有所类似,通过代码来解释:

    //创建一个信号,其中的参数为信号的初始值
    dispatch_semaphore_t singer = dispatch_semaphore_create(0);
    //发送信号,使信号量+1
    dispatch_semaphore_signal(singer);
    //等待信号,当信号量大于0时执行后面的方法,否则等待,第二个参数为等待的超时时长,下面设置的为一直等待
    dispatch_semaphore_wait(singer, DISPATCH_TIME_FOREVER);
    NSLog(@"123");

通过发送信号,可以试信号量+1,每次执行过等待信号后,信号量会-1;如此,我们可以很方便的控制不同队列中方法的执行流程。

5、挂起和开启任务队列

GCD还提供了暂停与开始任务的方法,使用

void dispatch_suspend(dispatch_object_t object);

可以将队列或者队列组进行暂时的挂起,使用

 

void dispatch_resume(dispatch_object_t object);

将队列或者队列组重新开启。

需要注意的是,暂停队列时,队列中正在执行的任务并不会被中断,会挂起未开启的任务。
 

四、关于内存管理

GCD虽然是基于C语言封装的框架,使用了面向对象的思想。因此,它的内存管理是需要我们注意的,不论是ARC或者MRC,我们都应该手动去处理这些对象。还好,GCD的内存管理思路和Object—C是兼容的,我们使用dispatch_retain()和dispatch_release()来将引用对象的计数进行加减。这一点十分重要,切记切记。

 

疏漏之处 欢迎指正

学习使用 欢迎转载

专注技术,热爱生活,交流技术,也做朋友。

——珲少 QQ群:203317592

© 著作权归作者所有

珲少

珲少

粉丝 891
博文 395
码字总数 478712
作品 0
上海
程序员
私信 提问
iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码

iOS精选源码 iOS高仿微信完整项目源码(http://www.code4app.com/thread-14695-1-1.html) Khala: Swift 编写的iOS/macOS 路由框架(http://www.code4app.com/thread-31586-1-1.html) 微信左滑删......

Android爱开源
01/03
45
0
关于iOS多线程,这边勉强可以看看(OC&Swift)

iOS开发多线程总是绕不过的坎,看了很多前辈们优秀的文章,如:关于iOS多线程,我说,你听,没准你就懂了!、谈iOS多线程(NSThread、NSOperation、GCD)编程、iOS多线程:『GCD』详尽总结、i...

Andy_Ron
2018/08/26
0
0
面试攻略:何为技术和年龄不匹配

最近帮人组建研发中心,面试了很多开发工程师,对“技术能力与工作年限是否匹配”的理解更深了,记录下来分享给大家。 为便于讨论,简单的依据工作年限,划分出 3 个阶段: 1 ~ 3 年 4 ~ 5 ...

imbrl71u7pt5x29rleu7
2018/04/18
0
0
iOS multiple threads(2)------GCD

前面一二也说过,iOS中多线程编程工具主要有: NSThread NSOperation GCD 这三种方法都简单易用,各有千秋.同NSThread相比,GCD的运行效率更高,开发更简单。 但GCD确是诱惑力的,因为其本身是...

辉兔狼
2013/03/24
579
0
如何判断你是合格的高级iOS开发工程师?

前言 随着移动互联网的高速发展泄洪而来,有意学习移动开发的人越来越多了,竞争也是越来越大,需要学习的东西很多。如何才能在激烈的移动开发者竞争中一枝独秀,成为一名真正合格的高级iOS...

_小迷糊
2018/05/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

在C语言中“静态”是什么意思?

我已经在C代码的不同地方看到了static一词。 这就像C#中的静态函数/类(实现在对象之间共享)吗? #1楼 多文件变量作用域示例 在这里,我说明了静态如何影响多个文件中函数定义的范围。 交流...

javail
13分钟前
3
0
利用 FC + OSS 快速搭建 Serverless 实时按需图像处理服务

作者:泽尘 简介 随着具有不同屏幕尺寸和分辨率设备的爆炸式增长,开发人员经常需要提供各种尺寸的图像,从而确保良好的用户体验。目前比较常见的做法是预先为一份图像存放多份具有不同尺寸的...

阿里巴巴云原生
15分钟前
2
0
前端架构最佳实践

Folders-by-Feature Structure 胜过 Folders-by-Type Structure

lilugirl
26分钟前
3
0
Seata AT 模式启动源码分析

从上一篇文章「分布式事务中间件Seata的设计原理」讲了下 Seata AT 模式的一些设计原理,从中也知道了 AT 模式的三个角色(RM、TM、TC),接下来我会更新 Seata 源码分析系列文章。今天就来分...

后端进阶
27分钟前
4
0
Python中“自我”一词的目的是什么?

Python中self词的目的是什么? 我知道它是指从该类创建的特定对象,但是我看不到为什么要将它显式地作为参数添加到每个函数中。 为了说明这一点,在Ruby中,我可以这样做: class myClass ...

技术盛宴
29分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部