美文网首页
GCD 详解

GCD 详解

作者: TyroneTang | 来源:发表于2017-05-04 09:44 被阅读24次

1、简介

GCD(Grand Central Dispatch)iOS和OSX中用于实现多线程编程的一套较低层的API,其功能有点像NSOperationQueue,但是它不属于cocoa 框架的内容。

2、使用GCD的优点或好处

易用:GCD比之Thread更简单易用。由于GCD基于work unit而非像Thread那样基于运算,所以GCD可以控制诸如等待任务结束监视文件描述符周期执行代码以及工作挂起等任务。基于block的特性导致它能极为简单的在不同代码作用域之间传递上下文。

效率:GCD被实现得如此轻量和优雅,使得它在很多地方比之专门创建消耗资源的线程更实用且快速。这关系到易用性:导致GCD易用的原因有一部分在于你可以不用担心太多的效率问题而仅仅使用它就行了。

性能:GCD自动根据系统负载来增减线程数量,这就减少了上下文切换以及增加了计算效率。

不再使用锁(Lock)用户队列可以用于替代锁来完成同步机制。在传统多线程编程中,你可能有一个对象要被多个线程使用,你需要一个锁来保护这个对象:

3、dispatch object与cocoa object的区别

GCD是纯c语言的,它被组建成面向对象的风格。GCD对象被称为dispatch_object,dispatch_object像Cocoa对象一样是引用计数的。使用dispatch_release和dispatch_retain函数来操作dispatch_object的引用计数来进行内存管理。可以查阅“man dispatch_object”来获取更多关于dispatch_object的信息。

4、GCD中的三种队列类型

The main queue:与主线程功能相同。实际上,提交至main queue的任务会在主线程中执行。main queue可以调用dispatch_get_main_queue()来获得。因为main queue是与主线程相关的,所以这是一个串行队列。

Global queues:全局队列是并发队列,并由整个进程共享。进程中存在三个全局队列:高、中(默认)、低三个优先级队列。可以调用dispatch_get_global_queue函数传入优先级来访问队列。

用户队列:用户队列 (GCD并不这样称呼这种队列, 但是没有一个特定的名字来形容这种队列,所以我们称其为用户队列) 是用函数dispatch_queue_create创建的队列. 这些队列是串行的。正因为如此,它们可以用来完成同步机制, 有点像传统线程中的mutex。

5、通过GCD高效利用多核心系统性能的两种方式

(1)、将单一任务或者一组相关任务并发至全局队列中运算

(2)、将多个不相关的任务或者关联不紧密的任务并发至用户队列中运算

全局队列

设想下面的循环:

for(id obj in array)

[self doSomethingIntensiveWith:obj];

假定-doSomethingIntensiveWith:是线程安全的且可以同时执行多个.一个array通常包含多个元素,这样的话,我们可以很简单地使用GCD来平行运算:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

for(id obj in array)

dispatch_async(queue, ^{

[self doSomethingIntensiveWith:obj];

});

如此简单,我们已经在多核心上运行这段代码了。

当然这段代码并不完美。有时候我们有一段代码要像这样操作一个数组,但是在操作完成后,我们还需要对操作结果进行其他操作:

for(id obj in array)

[self doSomethingIntensiveWith:obj];

[self doSomethingWith:array];

这时候使用GCD的dispatch_async就悲剧了.我们还不能简单地使用dispatch_sync来解决这个问题, 因为这将导致每个迭代器阻塞,就完全破坏了平行计算。

解决这个问题的一种方法是使用dispatch group。一个dispatch group可以用来将多个block组成一组以监测这些Block全部完成或者等待全部完成时发出的消息。使用函数dispatch_group_create来创建,然后使用函数dispatch_group_async来将block提交至一个dispatch queue,同时将它们添加至一个组。所以我们现在可以重新编码:

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 doSomethingIntensiveWith:obj];

});

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

dispatch_release(group);

[self doSomethingWith:array];

如果这些工作可以异步执行,那么我们可以更风骚一点,将函数-doSomethingWith:放在后台执行。我们使用dispatch_group_async函数建立一个block在组完成后执行:

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 doSomethingIntensiveWith:obj];

});

dispatch_group_notify(group, queue, ^{

[self doSomethingWith:array];

});

dispatch_release(group);

不仅所有数组元素都会被平行操作,后续的操作也会异步执行,并且这些异步运算都会将程序的其他部分的负载考虑在内。注意如果-doSomethingWith:需要在主线程中执行,比如操作GUI,那么我们只要将main queue而非全局队列传给dispatch_group_notify函数就行了。

对于同步执行,GCD提供了一个简化方法叫做dispatch_apply。这个函数调用单一block多次,并平行运算,然后等待所有运算结束,就像我们想要的那样:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

dispatch_apply([array count], queue, ^(size_t index){

[self doSomethingIntensiveWith:[array objectAtIndex:index]];

});

[self doSomethingWith:array];

这很棒,但是异步咋办?dispatch_apply函数可是没有异步版本的。但是我们使用的可是一个为异步而生的API啊!所以我们只要用dispatch_async函数将所有代码推到后台就行了:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

dispatch_async(queue, ^{

dispatch_apply([array count], queue, ^(size_t index){

[self doSomethingIntensiveWith:[array objectAtIndex:index]];

});

[self doSomethingWith:array];

});

这种方法的关键在于确定我们的代码是在一次对不同的数据片段进行相似的操作。如果你确定你的任务是线程安全的(不在本篇讨论范围内)那么你可以使用GCD来重写你的循环了,更平行更风骚。

要看到性能提升,你还得进行一大堆工作。比之线程,GCD是轻量和低负载的,但是将block提交至queue还是很消耗资源的——block需要被拷贝和入队,同时适当的工作线程需要被通知。不要将一张图片的每个像素作为一个block提交至队列,GCD的优点就半途夭折了。如果你不确定,那么请进行试验。将程序平行计算化是一种优化措施,在修改代码之前你必须再三思索,确定修改是有益的(还有确保你修改了正确的地方)。

6、Dispatch Sources

dispatch_source是一个监视某些类型事件的对象。当这些事件发生时,它自动将一个block放入一个dispatch_queue的执行例程中。看完目前还不是很理解,待续……

7、Dispatch Queue挂起|目标指定、信号量、单次初始化

挂起使用

dispatch队列不支持cancel(取消),没有实现dispatch_cancel()函数,不像NSOperationQueue,不得不说这是个小小的缺憾。

8、GCD中dispatch_set_target_queue的作用

(1)使用dispatch_set_target_queue更改Dispatch Queue的执行优先级

原文链接如下:http://www.dreamingwish.com/article/gcd-guide-dispatch-once-1.html

相关文章

  • iOS多线程:GCD Group、Barrier和NSOpera

    GCD系列文章 GCD任务和队列使用详解 本文主要总结GCD Group、Barrier和NSOperationQ...

  • Swift基础--线程队列

    参考:青玉伏案博客--GCD详解 线程 队列

  • iOS多线程之GCD详解

    原文链接:整理多线程:GCD详解,如有侵权立即删除 一、GCD简介 为什么要用GCD呢? 二、GCD任务和队列 2...

  • iOS GCD详解《二》

    GCD详解 《一》[https://www.jianshu.com/p/7e83243a12a6]iOS GCD信...

  • 详解GCD<2>-dispatch_barrier

    dispatch_barrier 讲完了GCD详解一。我们来看看一个不太常用的GCD。dispatch_barri...

  • iOS多线程 基本使用实例

    NSThread GCD GCD 详解:http://www.jianshu.com/p/d1f9b2a7b0a8...

  • GCD的使用

    GCD的详解iOS多线程--彻底学会多线程之『GCD』 GCD线程之间的通讯 一般在主线程里面刷新UI 点击、滚动...

  • GCD中的串行,并行,同步,异步详解

    gcd中的串行,并行,同步,异步详解 1、gcd中同步与异步的区别: gcd中的线程是由一个线程池来管理的,这个池...

  • GCD详解

    一 使用步骤 创建队列(串行队列或并发队列) 调用函数(同步或异步) 二 重要概念 1,同步和异步 同步或异步最大...

  • GCD详解

    学习 GCD 之前,先来了解 GCD 中两个核心概念:任务和队列。 任务:就是执行操作的意思,换句话说就是你在线程...

网友评论

      本文标题:GCD 详解

      本文链接:https://www.haomeiwen.com/subject/rmjrtxtx.html