前言

之前,咱们在探索动画及烘托相关原理的时分,咱们输出了几篇文章,解答了iOS动画是怎么烘托,特效是怎么作业的疑惑。咱们深感体系设计者在创作这些体系结构的时分,是如此脑洞大开,也 深深意识到了解一门技能的底层原理关于从事该方面作业的重要性。

因此咱们决议 进一步探求iOS底层原理的使命。继上一篇文章对GCDdispatch_get_global大局并发行列_queue+dispatch_sync同步函数dispatch_get_global大局并发行列_queue+dispatch_sync异步函数GCD单例GCD线程死锁探索之后,本篇文章将持续对GCD多线程底层原理的探索

一、栅门函数基本介绍dispatch_barrier_async与dispatch_barrier_sync

1.1 栅门函数的作用

栅门函数的作⽤

最直接的作⽤: 操控使命执⾏次序,也便是达到同步的作用

  • dispatch_barrier_async:前面的使命履行完毕,才会来到这儿
  • dispatch_barrier_sync:作用相同,可是这个会堵塞线程,影响后边的履行

留意 :栅门函数只能操控同一并发行列

1.2 栅门函数运用举例

  • dispatch_barrier_async 举例

dispatch_barrier_async举例

  • 运转成果如下:

操控台打印成果

  • 在同一个行列里边,栅门函数前面的使命履行完了,栅门函数里边的使命能够履行,可是不会堵塞线程
  • 栅门函数后边的使命仍是能够履行的。可是栅门函数前面的使命,是一定在栅门函数内部使命之前履行的。

也便是使命 1使命 2 是必然在栅门函数前面履行。

  • dispatch_barrier_sync

代码仍是👆上面的代码,便是把栅门函数异步改成同步了,看看会发生什么样的作用?

dispatch_barrier_sync举例

  • 操控台打印成果如下:

打印成果

  • 栅门函数前面的使命仍是正常履行了,可是后边的使命在栅门函数的后边履行
  • 栅门函数堵塞了线程,栅门函数后边的使命在栅门函数的使命履行完结,才会去履行

还记得上面的一句话吗:栅门函数只能操控同一并发行列,那么咱们试试不是同一个并发行列状况,栅门函数是否能够拦截住呢?

不是同一个行列状况举例

咱们把栅门函数放在了另一个并发的行列里边,发现并没有拦截住使命的履行,那么是不是异步的原因呢?

那么现在去改成同步看看能不能拦住呢?

不是同一个行列状况举例

从运转的成果来看,发现仍是拦不住,阐明不是同一个并发的行列,不论栅门函数是不是同步或者异步,都是拦截不住的,只能是同一个并发行列才能够!

咱们再来举个比如🌰,运用大局并发行列看看

大局并发行列举例

从打印成果来看,大局并发行列也是拦不住的,只能是自定义并发行列才能够,这是为什么呢?去底层源码看看是否能够找到答案!

二、 栅门函数源码剖析

2.1 流程盯梢

上面现已对栅门函数的作用有一个大致的知道,那么底层的完结逻辑是怎么样的呢?现在就去探索一下。

在源码里边搜索dispatch_barrier_sync,跟流程会走到_dispatch_barrier_sync_f — > _dispatch_barrier_sync_f_inline

_dispatch_barrier_sync_f_inline

这个_dispatch_barrier_sync_f_inline 办法咱们之前剖析死锁的时分来过这儿边,经过符号断点,这儿会走_dispatch_sync_f_slow办法,这儿设置了DC_FLAG_BARRIER的标签,对栅门做符号!

_dispatch_sync_f_slow

这儿也是之前同步发生死锁的时分来过的,经过下符号断点持续盯梢流程。

符号断点盯梢调试

由此盯梢的流程为:_dispatch_sync_f_slow –> _dispatch_sync_invoke_and_complete_recurse –> _dispatch_sync_complete_recurse,持续在源码里边盯梢发现定位到了这个_dispatch_sync_complete_recurse办法。

_dispatch_sync_complete_recurse

这儿是一个 do while循环,判别当时行列里边是否有barrier,有的话就dx_wakeup唤醒履行,直到使命履行完结了,才会履行_dispatch_lane_non_barrier_complete,表明当时行列使命现已履行完结了,而且没有栅门函数了就会持续往下面的流程走。

#define dx_wakeup(x, y, z) dx_vtable(x)->dq_wakeup(x, y, z)

那么现在去看看dq_wakeup

dq_wakeup

这儿咱们之前剖析同步和异步的时分也来过这儿,这儿大局并发的是 _dispatch_root_queue_wakeup,串行和并发的是_dispatch_lane_wakeup,那么两者有什么不一样呢?

2.2 自定义的并发行列剖析

咱们先去看看自定义的并发行列的_dispatch_lane_wakeup

_dispatch_lane_wakeup(dispatch_lane_class_t dqu, dispatch_qos_t qos,
		dispatch_wakeup_flags_t flags)
{
	dispatch_queue_wakeup_target_t target = DISPATCH_QUEUE_WAKEUP_NONE;
	if (unlikely(flags & DISPATCH_WAKEUP_BARRIER_COMPLETE)) {
		return _dispatch_lane_barrier_complete(dqu, qos, flags);
	}
	if (_dispatch_queue_class_probe(dqu)) {
		target = DISPATCH_QUEUE_WAKEUP_TARGET;
	}
	return _dispatch_queue_wakeup(dqu, qos, flags, target);
}
  • 判别是否为barrier方式的,会调用_dispatch_lane_barrier_complete办法处理
  • 假如没有barrier方式的,则走正常的并发行列流程,调用_dispatch_queue_wakeup办法。
  • _dispatch_lane_barrier_complete

_dispatch_lane_barrier_complete

  • 假如是串行行列,则会进行等候,等候其他的使命履行完结,再按次序履行
  • 假如是并发行列,则会调用_dispatch_lane_drain_non_barriers办法将栅门之前的使命履行完结。
  • 最终会调用_dispatch_lane_class_barrier_complete办法,也便是把栅门拔掉了,不拦了,从而履行栅门之后的使命。

_dispatch_lane_class_barrier_complete

2.3 大局并发行列剖析

  • 大局并发行列,dx_wakeup对应的是_dispatch_root_queue_wakeup办法,检查源码完结
void
_dispatch_root_queue_wakeup(dispatch_queue_global_t dq,
		DISPATCH_UNUSED dispatch_qos_t qos, dispatch_wakeup_flags_t flags)
{
	if (!(flags & DISPATCH_WAKEUP_BLOCK_WAIT)) {
		DISPATCH_INTERNAL_CRASH(dq->dq_priority,
				"Don't try to wake up or override a root queue");
	}
	if (flags & DISPATCH_WAKEUP_CONSUME_2) {
		return _dispatch_release_2_tailcall(dq);
	}
}
  • 大局并发行列这个里边,并没有对barrier的判别和处理,便是依照正常的并发行列来处理。
  • 大局并发行列为什么没有对栅门函数进行处理呢?由于大局并发行列除了被咱们运用,体系也在运用。
  • 假如添加了栅门函数,会导致行列运转的堵塞,从而影响体系级的运转,所以栅门函数也就不适用于大局并发行列。

三、 信号量dispatch_semaphore

3.1 信号量介绍

信号量在GCD中是指Dispatch Semaphore,是一种持有计数的信号的东西。有如下三个办法。

  • dispatch_semaphore_create : 创建信号量
  • dispatch_semaphore_wait : 信号量等候
  • dispatch_semaphore_signal : 信号量开释

3.2 信号量举例

在并发行列里边,能够运用信号量操控,最大并发数,如下代码:

信号量举例

  • 信号量举例打印成果

信号量举例打印成果

这儿一共创建了 4 个使命,异步并发履行,我在创建信号量的时分,设置了最大并发数为 2

  dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_semaphore_t sem = dispatch_semaphore_create(2);

从运转的动图,能够看到,每次都是两个使命一同履行了,打印的成果一目了然。

那么再举个比如看看,设置信号量并发数为0会怎么样呢?

设置信号量并发数为0 设置信号量并发数为0,就相当于加锁的作用,dispatch_semaphore_wait堵住了使命1让其等候,等使命 2履行完了,dispatch_semaphore_signal发送信号,我履行完了,你去履行吧!

这样到底信号量是怎么样等候,又是怎么样发送信号的呢?

3.3 信号量剖析

看看dispatch_semaphore_createapi的阐明

dispatch_semaphore_create

  • 当两个线程需求和谐特定事情的完结时,为该值传递0很有用。
  • 传递大于0的值关于管理有限的资源池很有用,其间池大小等于该值。
  • 信号量的起始值。 传递小于信号量的起始值。 传递小于零的值将导致回来 NULL。的值将导致回来 NULL,也便是小于0就不会正常履行。

总结来说,便是能够操控线程池中的最多并发数量

3.3.1 dispatch_semaphore_signal

  • dispatch_semaphore_signal

dispatch_semaphore_signal

  • dispatch_semaphore_signal里边os_atomic_inc2o原子操作自添加1,然后会判别,假如value > 0,就会回来0
  • 例如 value1 之后仍是小于 0,阐明是一个负数,也便是调用dispatch_semaphore_wait次数太多了,dispatch_semaphore_wait是做减操作的,等会后边会剖析。
  • 加一次后依然小于0就报反常"Unbalanced call to dispatch_semaphore_signal(),然后会调用_dispatch_semaphore_signal_slow办法的,做容错的处理,_dispatch_sema4_signal是一个do while 循环
_dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema)
{
	_dispatch_sema4_create(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
	_dispatch_sema4_signal(&dsema->dsema_sema, 1);
	return 1;
}
  • _dispatch_sema4_signal
void
_dispatch_sema4_signal(_dispatch_sema4_t *sema, long count)
{
	do {
		int ret = sem_post(sema);
		DISPATCH_SEMAPHORE_VERIFY_RET(ret);
	} while (--count);
}

3.3.2 dispatch_semaphore_wait

  • dispatch_semaphore_wait

dispatch_semaphore_wait 源码如下: dispatch_semaphore_wait

  • os_atomic_dec2o进行原子自减1操作,也便是对value值进行减操作,操控可并发数。
  • 假如可并发数为2,则调用该办法后,变为1,表明现在并发数为 1,剩下还可一起履行1个使命。假如初始值是0,减操作之后为负数,则会调用_dispatch_semaphore_wait_slow办法。

_dispatch_semaphore_wait_slow办法源码如下:

  • _dispatch_semaphore_wait_slow

_dispatch_semaphore_wait_slow

  • 这儿对dispatch_time_t timeout 进行判别处理,咱们前面的比如里边传的是DISPATCH_TIME_FOREVER,那么会调用_dispatch_sema4_wait办法
void
_dispatch_sema4_wait(_dispatch_sema4_t *sema)
{
	kern_return_t kr;
	do {
		kr = semaphore_wait(*sema);
	} while (kr == KERN_ABORTED);
	DISPATCH_SEMAPHORE_VERIFY_KR(kr);
}

_dispatch_sema4_wait办法里边是一个do-while循环,当不满足条件时,会一直循环下去,从而导致流程的堵塞。这也就解释了上面举例案里边的履行成果。

上面举例里边就相当于,下图中的状况

剖析

在上图框框的地方,① 相当于②,这儿是do-while循环,所以会履行使命 2使命 1一直在循环等候。

三、 总结

3.1 栅门函数

  • 运用栅门函数的时分,要和其他需求履行的使命必须在同一个行列中
  • 运用栅门函数不能运用大局并发行列
  • 除了咱们运用,体系也在运用。
  • 假如添加了栅门函数,会导致行列运转的堵塞,影响体系级的运转

3.2 信号量

  • dispatch_semaphore_wait 信号量等候,内部是对并发数做自减操作,假如为 小于 0,会履行_dispatch_semaphore_wait_slow然后调用_dispatch_sema4_wait是一个do-while,知道满足条件完毕循环
  • dispatch_semaphore_signal 信号量开释 ,内部是对并发数做自加操作,直到大于0时,为可操作
  • 坚持线程同步,将异步履行使命转换为同步履行使命
  • 确保线程安全,为线程加锁,相当于互斥锁

专题系列文章

1.前知识

  • 01-探求iOS底层原理|综述
  • 02-探求iOS底层原理|编译器LLVM项目【Clang、SwiftC、优化器、LLVM】
  • 03-探求iOS底层原理|LLDB
  • 04-探求iOS底层原理|ARM64汇编

2. 基于OC言语探索iOS底层原理

  • 05-探求iOS底层原理|OC的实质
  • 06-探求iOS底层原理|OC目标的实质
  • 07-探求iOS底层原理|几种OC目标【实例目标、类目标、元类】、目标的isa指针、superclass、目标的办法调用、Class的底层实质
  • 08-探求iOS底层原理|Category底层结构、App启动时Class与Category装载进程、load 和 initialize 履行、相关目标
  • 09-探求iOS底层原理|KVO
  • 10-探求iOS底层原理|KVC
  • 11-探求iOS底层原理|探索Block的实质|【Block的数据类型(实质)与内存布局、变量捕获、Block的品种、内存管理、Block的修饰符、循环引证】
  • 12-探求iOS底层原理|Runtime1【isa详解、class的结构、办法缓存cache_t】
  • 13-探求iOS底层原理|Runtime2【音讯处理(发送、转发)&&动态办法解析、super的实质】
  • 14-探求iOS底层原理|Runtime3【Runtime的相关使用】
  • 15-探求iOS底层原理|RunLoop【两种RunloopMode、RunLoopMode中的Source0、Source1、Timer、Observer】
  • 16-探求iOS底层原理|RunLoop的使用
  • 17-探求iOS底层原理|多线程技能的底层原理【GCD源码剖析1:主行列、串行行列&&并行行列、大局并发行列】
  • 18-探求iOS底层原理|多线程技能【GCD源码剖析1:dispatch_get_global_queue与dispatch_(a)sync、单例、线程死锁】
  • 19-探求iOS底层原理|多线程技能【GCD源码剖析2:栅门函数dispatch_barrier_(a)sync、信号量dispatch_semaphore】
  • 20-探求iOS底层原理|多线程技能【GCD源码剖析3:线程调度组dispatch_group、事情源dispatch Source】
  • 21-探求iOS底层原理|多线程技能【线程锁:自旋锁、互斥锁、递归锁】
  • 22-探求iOS底层原理|多线程技能【原子锁atomic、gcd Timer、NSTimer、CADisplayLink】
  • 23-探求iOS底层原理|内存管理【Mach-O文件、Tagged Pointer、目标的内存管理、copy、引证计数、weak指针、autorelease

3. 基于Swift言语探索iOS底层原理

关于函数枚举可选项结构体闭包属性办法swift多态原理StringArrayDictionary引证计数MetaData等Swift基本语法和相关的底层原理文章有如下几篇:

  • Swift5中心语法1-基础语法
  • Swift5中心语法2-面向目标语法1
  • Swift5中心语法2-面向目标语法2
  • Swift5常用中心语法3-其它常用语法
  • Swift5使用实践常用技能点

其它底层原理专题

1.底层原理相关专题

  • 01-计算机原理|计算机图形烘托原理这篇文章
  • 02-计算机原理|移动终端屏幕成像与卡顿 

2.iOS相关专题

  • 01-iOS底层原理|iOS的各个烘托结构以及iOS图层烘托原理
  • 02-iOS底层原理|iOS动画烘托原理
  • 03-iOS底层原理|iOS OffScreen Rendering 离屏烘托原理
  • 04-iOS底层原理|因CPU、GPU资源耗费导致卡顿的原因和解决计划

3.webApp相关专题

  • 01-Web和类RN大前端的烘托原理

4.跨渠道开发计划相关专题

  • 01-Flutter页面烘托原理

5.阶段性总结:Native、WebApp、跨渠道开发三种计划性能比较

  • 01-Native、WebApp、跨渠道开发三种计划性能比较

6.Android、HarmonyOS页面烘托专题

  • 01-Android页面烘托原理
  • 02-HarmonyOS页面烘托原理 (待输出)

7.小程序页面烘托专题

  • 01-小程序结构烘托原理