GCD --换种简单的方式理解

作者: 李周 | 来源:发表于2017-11-26 15:08 被阅读160次

在开发的过程中,对于多线程的需求越来越高,但是由于在概念的理解上一直都有些许的模糊不清,所以这次也趁着重读一遍高级编程中的GCD部分,将多线程重新整理了一下。


1. 多线程的理解
2. GCD方法的理解

1. 多线程的理解

多线程的本义很好理解:就是为了多个不同的任务开启多个线程,让主线程能及时的响应用户的操作。但是对于和多线程紧紧相关的异步、同步和并行、串行而言,很容易搞模糊。所以下面还是按照自己的思路一步步进行分析:

1)队列

官方点的理解:队列就是任务的集合,采用的是先进先出的原则,每执行完一个任务就会释放一个任务。通白点理解就是:对于每一个新任务而言,先排到队伍的末尾,具体该如何执行等按序轮到才知道。

2)同步与异步

① 同步:在当前线程中执行任务,并没有开启新线程的能力
② 异步:具有开启新线程的能力,能在新的线程中执行任务

首先,通过图例解释异步执行的过程:


异步执行示范

异步执行并不会堵塞当前的线程,只是告诉当前的线程:

你只要帮我将任务加入queue队列中即可,任务该如何执行无你无关。

所以从上面图例中的主线程视角看整个的执行过程,就是:


异步执行时主线程视角

主线程并不会直接看到block中关于任务的内容,只是将dispatch_async和NSLog都当做一行执行命令来执行。也就是说:主线程只管执行各个命令行,而队列中加入的任务在其他线程中执行:


异步执行打印结果

同步执行正好和异步执行相反,以下面的图例分析:


同步执行示范
不仅需要将任务加入到queue队列中,还会强制要求当前线程暂停,来执行该队列中的任务。

所以结果和执行的顺序一致:


同步执行的打印结果
3)队列类型:串行和并行

① 串行 --->一个任务执行完再执行下一个任务,按序一个个执行。

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

① 并行 --->不等待当前任务执行完就执行下一个任务。

dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);

同步还是异步、串行还是并行对于任务而言就相当于两个同等重要的原则。用商店的收银台为例来理解:

一家商店的收银处,同步表示只有一个收银台,而异步表示有多个收银台。而串行表示管你有几个
收银台,所有人就排成一队;而并行表示尽量分散,有几个排几个。

在对多线程的处理过程中,同步执行其实是很容易出错的地方。个人理解就是:在当前线程正在处理“将任务加入queue队列”中时,同步执行会强制性的要求当前线程暂停手中的任务,从而用所有的资源来执行"queue队列中的任务"。举例而言:


同步执行易报错点

主线程正在执行任务时,强制要求执行block中的新任务。


同步执行易报错点

异步执行创建的线程正在执行任务,强制要求执行同步中的任务。

所以在对同步执行的使用中,要特别注意不要进入到崩溃的逻辑中。

2. GCD方法的理解
dispatch_after
dispatch_after示例

对于该方法而言,有两个地方需要特别注意:
① NSEC_PER_SEC
dispatch_afer方法中的第二个参数的时间单位并不是秒而是"纳秒",所以这也是为什么需要使用系统定义的变量进行转换的原因。系统提供的装换方式:

每秒有多少纳秒
#define NSEC_PER_SEC 1000000000ull 
每微妙有过少毫秒
#define NSEC_PER_MSEC 1000000ull
每秒有多少毫秒
#define USEC_PER_SEC 1000000ull
每毫秒有多少纳秒
#define NSEC_PER_USEC 1000ull

②4秒之后执行?
dispatch_after并不是在指定时间后执行处理,而只是在指定时间内将任务追加到Queue队列中。
对于上面的图例而言,该队列是在主线程的RunLoop中执行,而主线程的RunLoop每隔1/60秒执行,而该Block中的任务要求延迟4秒之后执行,所以最快在4秒 +1/60后执行。并且由于主队列中有大量的处理或主线程本身就有一定的延迟,所以执行的时间会更长。

所以,dispatch_afer只适用于一些想大致延迟执行的处理中,并不适用于对时间有严格要求的情况下。
dispatch_barrier_async

对于多线程而言,最需要关注的就是数据竞争的问题。虽然串行执行能避免该问题,但是现实生活中数据的读取往往是要求能并行执行的,只有当遇到对数据的写入时才需要额外的关注。
举例而言:


并行执行读取和写入操作

很显然,读取和写入并行的情况下,读取的数据会出现错误:


简单的并行执行数据读取出错

而dispatch_barrier_async的定义就是:
等待之前的并行执行全部结束之后,再将指定的处理追加到队列中。等待该函数追加的处理执行完毕之后,队列才会恢复到一般的动作:


执行结果
所以,dispatch_barrier_async就是等待之前的并行操作执行之后,再单独的执行该函数中的任务处理,完毕之后就是普通的执行。
Dispatch Semaphore

在实际的开发过程中,经常会出现一种需求:在同一个界面中需要多次请求,在所有请求结束之后再刷新界面。在此之前,我一直都很low的用在一个请求结束时嵌入另一个请求的方法,直到认识到该方法:


信号量的使用

上面的图例有几个需要知道的地方:
①dispatch_semaphore_t
Dispatch Semaphore是持有计数的信号,计数为0时等待,计数为1或大于1时,减去1而继续执行。

首先,创建信号时赋值计数的初始值为0

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

其次,当信号量遇到wait方法时,判断计数是否大于等于1。如果等于0,进入等待状态,所以等价于该线程暂停在该处。所以:

[task resume]必须写在wait方法之前

最后,当任务执行结束,signal函数会将计数值加1,此时wait函数会识别到计数不为0,会减1继续向下执行。

dispatch_semaphore_signal(semaphore);

信号量配合group使用,当所有的请求结束时,通知主线程进行界面的刷新:


通知主线程进行界面刷新

对于类似多个网络请求的情况,GCD还提供了另外的一种方法:


多个网络请求第二种方法
dispatch_group_enter(group);
dispatch_group_leave(group);

关于越底层的东西,越值得深入的思考。GCD的理解也算是基本的入门,也想要更多的去了解这些东西。冬天到了,得好好学习了。

相关文章

  • GCD --换种简单的方式理解

    在开发的过程中,对于多线程的需求越来越高,但是由于在概念的理解上一直都有些许的模糊不清,所以这次也趁着重读一遍高级...

  • 如何取消GCD任务

    转载自:教你如何取消GCD任务 GCD 是一种非常方便的使用多线程的方式。通过使用 GCD,我们可以在确保尽量简单...

  • iOS多线程的简单用法

    实现多线程的方式一般有三种:NSThread、GCD、NSOperation 1、NSThread:适用简单,简单...

  • GCD - objective C

    GCD是一种比写底层thread code更简单的管理thread的方式。 有三种dispatch queue,它...

  • NSOperation与GCD区别

    一.区别 GCD的核心是C语言写的系统服务,执行和操作简单高效,因此NSOperation底层也通过GCD实现,换...

  • IOS基础之切回到主线程的N种方式

    方式一:GCD (最常用) 使用GCD又有2种方式:异步回到主线程和同步回到主线程。2种方式的差别在于是否会阻塞原...

  • IOS基础之切回到主线程的N种方式

    方式一:GCD (最常用) 使用GCD又有2种方式:异步回到主线程和同步回到主线程。2种方式的差别在于是否会阻塞原...

  • swift的多线程

    线程的方式有多种多样,主要包含以下三种:Thread、GCD、OperationQueue,下面我们简单的介绍一下...

  • 三种不同的方式实现倒计时的Demo

    分别采用NSTimer、CADisplayLink、GCD时间源的方式实现简单的倒计时,本来还有第四种使用异步线程...

  • 换种方式,换种心情

    不知道今天你有没有报名呢?时间总是偷偷溜走,在你想不到的任何时刻,今天没有加班。 他去了安阳找朋友玩,我真的不知道...

网友评论

    本文标题:GCD --换种简单的方式理解

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