dispatch_semaphore
概述
dispatch_semaphore是持有计数的信号,该信号是多线程编程中的计数类型信号。有3个api:create
, wait
signal
。
使用
- 对常用资源进行加锁操作,防止多线程访问修改数据导致出现结果不一致甚至崩溃的问题:
1 | //在init等函数初始化 |
- 串行执行一系列异步任务
1 | - (void)chainRequestCurrentConfig { |
源码分析
dispatch_semaphore_t
先看一下dispatch_semaphore_s
的定义
1 | struct dispatch_semaphore_s { |
dispatch_semaphore_create
dispatch_semaphore_create
用来创建信号量,创建时要指定value,内部会将value的值存储到 dsema_value
(当前值) 和 dsema_orig
(初始值) 中,value的值必须大于等于0。
1 | dispatch_semaphore_t dispatch_semaphore_create(long value) { |
接着来看Dispatch Semaphore很容易忽略也是最容易造成App崩溃的地方,即信号量的释放。
创建Semaphore
时,会将do_vtable 指向_dispatch_semaphore_vtable
, _dispatch_semaphore_vtable
的结构体定义了信号量销毁时会执行_dispatch_semaphore_dispose
方法。相关代码如下:
1 | //semaphore的vtable定义 |
1 | //释放信号量的函数 |
如果销毁时信号量还在使用,那么dsema_value
值会小于dsema_orig
,引起崩溃。如下代码:1
2
3
4dispatch_semaphore_t semephore = dispatch_semaphore_create(1);
dispatch_semaphore_wait(semephore, DISPATCH_TIME_FOREVER);
//重新赋值或者将semephore = nil都会造成崩溃,因为此时信号量还在使用中
semephore = dispatch_semaphore_create(0);
1 | - (void)func { |
dispatch_semaphore_wait
1 | long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout){ |
1 | static long _dispatch_semaphore_wait_slow(dispatch_semaphore_t dsema, |
_dispatch_semaphore_wait_slow
根据timeout类型分为三种情况:
- DISPATCH_TIME_NOW: 如果
dsema_value
小于0,对其+1并返回超时信号KERN_OPERATION_TIMED_OUT
- DISPATCH_TIME_FOREVER: 调用系统
semaphore_wait
方法,直到收到signal
调用。 - default:调用内核方法semaphore_timedwait计时等待,直到有信号到来或者超时了。
dispatch_semaphore_signal
1 | long dispatch_semaphore_signal(dispatch_semaphore_t dsema) { |
首先将semaphore_value
原子加一,如果大于0直接返回0,否则进入_dispatch_semaphore_signal_slow
方法,该函数会调用内核的semaphore_signal
函数唤醒等待中的线程。
1 | long _dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema) { |
总结
Dispatch Semaphore 信号量wait方法会将信号量值减1,如果大于等于0就立即返回,否则等待信号量唤醒或超时;signal方法会将信号量值加1,如果value大于0立即返回,否则会唤醒某个等待中的线程。
需要注意信号量销毁或重新创建的时候如果还在使用会引起崩溃。