一、什么是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. hive中一般取top n时,row_number(),rank,dense_ran()常用三个函数

    一. 分区函数Partition By与row_number().rank().dense_rank()的用法(获取分组(分区)中前几条记录) 一.数据准备 --1.创建学生成绩表 id int,   ...

  2. flume集成hdfs(hdfs开启kerberos认证)

    )当 sink 到 hdfs 时: ) 需修改 flume-env.sh 配置,增添 hdfs 依赖库: FLUME_CLASSPATH="/root/TDH-Client/hadoop/h ...

  3. [leetcode] 周赛 223

    比赛题目:https://leetcode-cn.com/contest/weekly-contest-223/. 解码异或后的数组 题目:1720. 解码异或后的数组. 还记得数列求和的「累加法」? ...

  4. LeetCode105 从前序和中序序列构造二叉树

    题目描述: 根据一棵树的前序遍历与中序遍历构造二叉树. 注意:你可以假设树中没有重复的元素. 例如,给出 前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9 ...

  5. Linux调整lvm逻辑分区大小

    转载自:https://www.cnblogs.com/kevingrace/p/5825963.html  个人记录一下   Linux下对lvm逻辑卷分区大小的调整(针对xfs和ext4不同文件系 ...

  6. 【Linux】ABRT has detected 1 problem(s). For more info run: abrt-cli list --since 1548988705

    ------------------------------------------------------------------------------------------------- | ...

  7. C语言补码(C语言学习笔记)

    记录 在学习C语言数据范围时了解到了补码的概念,记录一下什么是补码,补码怎么运算的 运算 原文链接:https://www.cnblogs.com/lsgsanxiao/p/5113305.html ...

  8. oracle 12C单实例打PSU

    前提: oracle不管打什么样的补丁,readme都是很好的参考资料. Oracle每季度都会更新一个最新的PSU,现在12.1.0.2.0的最新的PSU是Patch 26925311. 由于今天白 ...

  9. sap的内核升级,修补了源代码保护的方式

    众所周知,在SAP的内核位701或者之前的版本中,我们可以通过向源代码的中加入"*@#@@[SAP]"这样的代码,来实现对源代码的保护.但是在内核升级到721和以后的版本中,你会发 ...

  10. 日常分享:关于时间复杂度和空间复杂度的一些优化心得分享(C#)

    前言 今天分享一下日常工作中遇到的性能问题和解决方案,比较零碎,后续会持续更新(运行环境为.net core 3.1) 本次分享的案例都是由实际生产而来,经过简化后作为举例 Part 1(作为简单数据 ...