前言
上一篇详细介绍了介绍了GCD中的常用API,
考虑到篇幅问题,这里继续介绍另外的两个API。
Dispatch Semaphore 信号量
dispatch_semaphore_t
信号量本质上是一种锁。
关于iOS中各种锁和性能比较可以看下yykit作者的这篇博文,戳这里
不再安全的 OSSpinLock
下面我们看下信号量的使用:dispatch_semaphore_t
的作用之一解决资源抢夺问题
之前提过,对于数据存储类似数据库,非原子性可变字典和可变数组等多线程下不安全的操作,可以使用同步队列保证线程安全,那么在并发队列中,可以使用信号量来解决资源抢夺问题
1 | //全局队列 |
代码解读一下:dispatch_semaphore_create(1)
创建了值为1信号量dispatch_semaphore_wait
,如果信号量的值大于等于1,那么,信号量值减1,然后向下执行,如果信号量值为0,一直等待。直到大于等于1的时候,率先进入等待状态的异步队列率先执行dispatch_semaphore_signal
信号量值加1
形象比喻一下:
一群人排队去银行办业务,银行初始只有一个窗口,第一个人办业务的时候,可用窗口就变成0个了,这个人办完业务,可用窗口加1,就变成1个了。
实际这种效果和加锁的本质一致。dispatch_semaphore_t
的另外一个作用就是可以控制线程并发数量,之前我们提过,iOS7之后系统自动开辟的线程数量可以多达60-70,而GCD中并没有提供控制线程数量的API,NSOperation中可以设置最大线程数。
下面我们使用信号量来实现一下线程数量控制:
1 | //线程并发数限制 |
dispatch source
dispatch source
是一组不常用的GCD API。是BSD系内核惯有功能kqueue的包装。kqueue的介绍可以看下这个kqueue wikipedia
简单来说,dispatch source是一个监视某些类型事件的对象。它支持所有kqueue所支持的事件以及mach(mach介绍可以看这里mach wikipedia))端口、内建计时器支持和用户事件,CPU负荷占用小,资源占用小。
dispatch source
联结
联结的流程:在任一线程上调用dispatch_source_merge_data 这个函数后,会执行 Dispatch Source 事先定义好的句柄(可以简单理解句柄就是block )(是不是有点通知,回调的味道哈)
下面直接上代码:
1 | //全局队列 |
这段程序简单逻辑:调用dispatch_source_merge_data
会触发实现定义好的事件
1 | dispatch_source_set_event_handler(source, ^{ |
dispatch_source_create
函数参数
DISPATCH_SOURCE_TYPE_DATA_ADD
累加
当注册系统事件的时候,有时候系统还没来得及通知应用程序,这个时候,系统会累计传递过来的值
DISPATCH_SOURCE_TYPE_DATA_OR
逻辑或处理累计传递过来的值
其他:
DISPATCH_SOURCE_TYPE_MACH_SEND
MACH端口发送DISPATCH_SOURCE_TYPE_MACH_RECV
MACH端口接收DISPATCH_SOURCE_TYPE_PROC
监测进程相关事件DISPATCH_SOURCE_TYPE_READ
可读取文件映像DISPATCH_SOURCE_TYPE_SIGNAL
接收信号DISPATCH_SOURCE_TYPE_TIMER
定时器DISPATCH_SOURCE_TYPE_VNODE
文件系统变更DISPATCH_SOURCE_TYPE_WRITE
可写入文件映像
注册事件处理程序通知,如果系统没来的及通知应用程序时候事件发生多次,这些事件会合并为一个事件(是不是类似于TCP协议中的nagle算法)。iOS开发者通常不会用到这种功能。但对于底层,这种处理方式会很高效.
简单流程总结:创建一个源,自定义累计方式,可以是and也可以是Or,自定义源也需要一个队列用来处理响应块,可以是主队列,也可以是并发队列。
在同一时间,只有一个响应块被分派。处理方法没执行完毕,另一个事件发生,事件以指定方式(ADD或者OR)进行累积。通过合并,保证了在高负载下稳定执行。
累计值通过 dispatch_source_get_data
获取。每次响应执行事件,这个值会被重置dispatch_source_merge_data
发送一个事件
默认创建出来的source是挂起状态的,需要调用dispatch_resume
才可生效
除了高效的自定义一个source处理自定事件之外,我们也可以使用dispatch_source
来定义一个定时器,iOS开发中常用的定时器有NSTimer
和CADisplayLink
两种NSTimer
受到runloop的状态影响精度CADisplayLink
则和屏幕刷新度帧数一致
而dispatch_source
作为定时器精度很高,是系统级别的源
demo如下:
1 | dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); |
总结
本文主要介绍了
dispatch_semaphore_t
,本质是一种底层锁,性能较高,可以用来解决多线程资源竞争,控制线程并发数。
dispatch source
最大的优势是联结,通过合并事件的方式,高效的处理事件分派,可以自定义source用来处理高负载应用场景响应。
dispatch source
可以作为高精度,系统源层级的定时器,在需要高精度应用场景下可以选用这种更加接近底层的定时器。
写了两篇博客来详解了下GCD,主要是对自我基础的一个总结。之前已经有很多写的很好的GCD文章。关于信号量和dispatch_source
还有兴趣深入了解的可以阅读下官方文档。
推荐下阅读猿神的博客
Parse源码浅析系列(一)—Parse的底层多线程处理思路:GCD高级用法
参考书籍:
Objective-C高级编程