一、什么是GCD?

以下是摘自苹果的官方说明。

Grand Central Dispatch(GCD)是异步执行任务的技术之一。一般将应用程序中记述的线程管理用的代码在系统级中实现。开发者只需要定义想执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。

二、GCD实现之Dispatch Queue

  • 用于管理追加的Block的C语言层实现的FIFO队列
  • Atomic函数中实现的用于排他控制的轻量级信号
  • 用于管理线程的C语言层实现的一些容器

GCD的API全部包含在libdispatch库中的C语言函数。Dispatch Queue通过结构体和链表,被实现为FIFO队列。FIFO队列管理是通过dispatch_async等函数所追加的Block。

Block并不是直接加入FIFO队列,而是先加入Dispatch Continuation这一dispatch_continution_t类结构体中,然后再加入FIFO队列。该Dispatch Continuation用于记忆Block所属的Dispatch Group和其他一些信息,相当于一般常说的执行上下文。

Dispatch Queue可通过dispatch_set_target_queue函数设定,可以设定执行该Dispatch Queue处理的Dispatch Queue为目标。该目标可像串珠子一样,设定多个连接在一起的Dispatch Queue。但是在连接串的最后必须设定为Main Dispatch Queue,或各种优先级的Global Dispatch Queue,或是准备用于Serial Dispatch Queue的各种优先级的 Global Dispatch Queue.

Global Dispatch Queue有如下8种。

  • Global Dispatch Queue (High Priority)
  • Global Dispatch Queue (Default Priority)
  • Global Dispatch Queue (Low Priority)
  • Global Dispatch Queue (Background Priority)
  • Global Dispatch Queue (High Overcommit Priority)
  • Global Dispatch Queue (Default Overcommit Priority)
  • Global Dispatch Queue (Low Overcommit Priority)
  • Global Dispatch Queue (Background Overcommit Priority)

优先级中附有Overcommit的Global Dispatch Queue使用在Serial Dispatch Queue中。如Overcommit这个名称所示,不管系统状态如何,都会强制生成线程的Dispatch Queue。

这8种Global Dispatch Queue各使用1个pthread_workqueue。GCD初始化时,使用pthread_workqueue_create_np函数生成pthread_workqueue。

pthread_workqueue包含在Libc提供的pthreads API中。其使用bsdthread_register和workq_open系统调用,在初始化XNU内核的workqueue之后获取workqueue信息。

XNU内核持有4中workqueue。

  • WORKQUEUE_HIGH_PRIOQUEUE
  • WORKQUEUE_DEFAULT_PRIOQUEUE
  • WORKQUEUE_LOW_PRIOQUEUE
  • WORKQUEUE_BG_PRIOQUEUE

以上为4种执行优先级的workqueue。该执行优先级与Global Dispatch Queue的4种执行优先级相同。

Dispatch Queue中执行Block的过程。

当在Global Dispatch Queue中执行Block时,libdispatch从Global Dispatch Queue自身的FIFO队列中取出Dispatch Continuation,调用pthread_workqueue_additem_np函数。将该Global Dispatch Queue自身、符合其优先级的workqueue信息以及为执行Dispatch Continuation的回调函数等传递给参数。

pthread_workqueue_additem_np函数使用workq_kernreturn系统调用,通知workqueue增加应当执行的项目。根据该通知,XNU内核基于系统状态判断是否需要生成线程。如果是Overcommit优先级的Global Dispatch Queue,workqueue则始终生成线程。

workqueue的线程执行pthread_workqueue函数,该函数调用libdispatch的回调函数。在该回调函数中执行加入到Dispatch Continuation的Block。

Block执行结束后,进行通知Dispatch Group结束、释放Dispatch Continuation等处理,开始准备执行加入到Global Dispatch Queue中的下一个Block。

以上就是Dispatch Queue执行的大概过程。

三、线程和队列

线程是代码执行的路径,队列则是用于保存以及管理任务的,线程负责去队列中取任务进行执行。

1、队列

是管理线程的,相当于线程池,能管理线程什么时候执行。

队列分为串行队列和并行队列等

串行队列:队列中的任务按顺序执行

并行队列:队列中的任务会并发执行。任务执行完毕了,不一定出队列。只有前面的任务执行完了,才会出队列。

串行队列:队列中的任务只会顺序执行,多个串行队列可并行执行

dispatch_queue_t q = dispatch_queue_create(“xxx”,DISPATCH_QUEUE_SERIAL);

并行队列:队列中的任务会并发执行

dispatch_queue_t q = dispatch_queue_create(“xxx”, DISPATCH_QUEUE_CONCURRENT);

全局队列:与并行队列类似,但调试时,无法确认操作所在队列

dispatch_queue_t q = dispatch_get_global_queue(dispatch_queue_priority_default, 0);

主队列:每一个程序对应唯一一个主队列;在多线程开发中,使用主队列更新UI

dispatch_queue_t q = dispatch_get_main_queue();

2、同步和异步

dispatch_async (异步操作函数),就是将指定的Block“非同步”地追加到指定的队列(queue)中。dispatch_async函数不做任何等待。会新开线程

dispatch_sync( 同步操作函数),就是将指定的Block“同步”地追加到指定的队列(queue)中。在追加Block结束之前,dispatch_sync函数会一直等待;不会新开线程

3、队列和操作的组合

串行队列同步操作:同步操作不会新开线程、操作顺序执行

串行队列异步操作:异步操作新开一个子线程、操作顺序执行,“最安全的选择”

并行队列同步操作:同步操作不会新开线程、操作顺序执行

并行队列异步操作:异步操作会新开多个线程(有多少任务,就开n个线程执行)、操作无序执行;队列前如果有其他任务,会等待前面的任务完成之后再执行;场景:既不影响主线程,又不需要顺序执行的操作!

全局队列异步操作:异步操作会新建多个线程、操作无序执行,队列前如果有其他任务,会等待前面的任务完成之后再执行

全局队列同步操作:同步操作不会新建线程、操作顺序执行

主队列异步操作:异步操作都在主线程上顺序执行的,不存在异步的概念

主队列同步操作:会死锁

4、会引起死锁的2种情况

1、在主线程中运用主队列同步。

- (void)viewDidLoad {

[super viewDidLoad];

dispatch_sync(dispatch_get_main_queue(), ^{

NSLog(@"hello");

});

}

同步对于任务是立刻执行的,那么当把任务放进主队列时,它就会立马执行,只有执行完这个任务,viewDidLoad才会继续向下执行。

而viewDidLoad和任务都是在主队列上的,由于队列的先进先出原则,任务又需等待viewDidLoad执行完毕后才能继续执行,viewDidLoad和这个任务就形成了相互循环等待,就造成了死锁。

想避免这种死锁,可以将同步改成异步dispatch_async,或者将dispatch_get_main_queue换成其他串行或并行队列,都可以解决。

2、在串行队列中同步的向这个串行队列追加Block

dispatch_queue_t serialQueue = dispatch_queue_create("xxx", DISPATCH_QUEUE_SERIAL);

dispatch_async(serialQueue, ^{

dispatch_sync(serialQueue, ^{

NSLog(@"hello");

});

});

想避免这种死锁,可以将同步改成异步dispatch_async,或者将串行队列换为并行队列,都可以解决。

以上部分内容参考自《Objective-C高级编程》一书

GCD之队列的实现和使用的更多相关文章

  1. iOS边练边学--GCD的基本使用、GCD各种队列、GCD线程间通信、GCD常用函数、GCD迭代以及GCD队列组

    一.GCD的基本使用 <1>GCD简介 什么是GCD 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数   GCD的优势 G ...

  2. ios线程和GCD和队列同步异步的关系

    1.什么是进程? 进程是指在系统中正在运行的一个应用程序.比如同时打开QQ.Xcode,系统就会分别启动2个进程.截图 2.什么是线程? 1).一个进程要想执行任务,必须得有线程(每一个进程至少要有一 ...

  3. iOS 关于GCD中的队列

    GCD中队列分类及获得方式 1.串行队列  dispatch_queue_t queue = dispatch_queue_create("队列名", DISPATCH_QUEUE ...

  4. ios多线程操作(五)—— GCD串行队列与并发队列

          GCD的队列能够分为2大类型,分别为串行队列和并发队列      串行队列(Serial Dispatch Queue):      一次仅仅调度一个任务,队列中的任务一个接着一个地运行( ...

  5. AJ学IOS(50)多线程网络之GCD简单介绍(任务,队列)

    AJ分享,必须精品 GCD简单介绍 1.什么是GCD? 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 2.GCD的优势 GCD是苹果 ...

  6. iOS 多线程GCD简介

    一.简介 1.1 GCD (Grand Central Dispatch )是Apple开发的一个多核编程的解决方法. Grand 含义是“伟大的.宏大的”,Central含义“中央的”,Dispat ...

  7. iOS GCD 编程小结

    一.简单介绍 1.GCD简介? 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 2.GCD优势 GCD是苹果公司为多核的并行运算提出的 ...

  8. GCD: 基本概念和Dispatch Queue 【转】

    什么是GCD? Grand Central Dispatch或者GCD,是一套低层API,提供了一种新的方法来进行并发程序编写.从基本功能上讲,GCD有点像 NSOperationQueue,他们都允 ...

  9. IOS 多线程02-pthread 、 NSThread 、GCD 、NSOperationQueue、NSRunLoop

    注:本人是翻译过来,并且加上本人的一点见解. 要点: 1.前言 2.pthread 3.NSThread 4.Grand Central Dispatch(GCD) 5.Operation Queue ...

随机推荐

  1. Head First 设计模式 —— 07. 适配器模式

    思考题 你能想到真实世界中,还有哪些适配器的例子? P236 HDMI 转 VGA 转换器 Type-C 转 3.5mm 线 适配器模式解析 客户使用适配器的过程: P241 客户通过目标接口调用适配 ...

  2. String--常见面试题

    String s = new String("xyz") 创建了几个对象? 实例分析1 javac编译代码,然后用javap来反编译,执行javap -c Test 从结果来看,l ...

  3. TCP连接的建立与释放(超详细)

    前言:在计算机网络协议中,TCP只是其中一个,然而在网络使用中,TCP也是最离不开的协议之一,它的重要性毋庸置疑,最最重要的是,面试的重点就是它啊,呜呜~~,今天我们一起来看下TCP的连接建立与释放, ...

  4. (十七)logging模块

    logging模块是Python内置的标准模块,主要用于输出运行日志. 简单应用 import logging logging.debug('+++debug+++') logging.info('+ ...

  5. LeetCode202. 快乐数

    题目 编写一个算法来判断一个数 n 是不是快乐数. 快乐数定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1, 也可能是 无限循环 但始终变不到 ...

  6. fileinput模块用法

    fileinput模块功能: 提供拼接一个或多个文本文件的功能,可以通过使用for循环来读取一个或多个文本文件的所有行,从而进行逐行处理(如进行显示.替换.添加行号等). 其功能类似于linux命令的 ...

  7. Linux服务器上迁移项目路径,修改nginx配置,迁移及备份MongoDB数据库流程 (超详细)!!!

    缘由:客户服务器项目路径不是很合理,导致Jenkins自动部署时还需要添加路径后再更新部署,所以需要把项目路径统一和规范化. 迁移项目路径,保证路径合规,同时做好备份和迁移.迁移后先安装好依赖. 项目 ...

  8. 30分钟带你理解 Raft 算法

    为什么需要 Raft? Raft 是什么? Raft 的目标 前置条件:复制状态机 Raft 基础 Leader 选举(选举安全特性) 日志复制(Leader只附加.日志匹配) 安全 学习资料 使用 ...

  9. Python小度

    这只是一个对话器!还不能听歌(反正我也没在UNIT平台配置听歌的功能)! 反正最近也不知怎么的,就想做一个AI对话器语音识别和语音输出都不要,input()和print()就行本来准备用小爱的,但要实 ...

  10. SpringBoot单元测试的两种形式

    @ 目录 前言 demo环境 springbootTest Junit 总结 前言 最近公司要求2021年所有的项目代码单元测试覆盖率要达到90%,作为刚毕业的小白来说这简直就是噩梦啊,springb ...