返回
深入剖析 GCD:dispatch_once 和 dispatch_semaphore 的源码解析
IOS
2023-11-18 17:23:03
在 iOS 多线程的知识体系中,GCD(Grand Central Dispatch)扮演着至关重要的角色。本文将深入探究 GCD 源码中两个关键函数:dispatch_once 和 dispatch_semaphore,为您提供对 GCD 内部机制的深入了解。
揭开 dispatch_once 的神秘面纱
dispatch_once 函数旨在确保一个任务仅被执行一次,即使在多线程并发调用时也能保证线程安全。这一特性使其在创建单例、swizzeld method 等场景中发挥着至关重要的作用。
源码解析
DISPATCH_ALWAYS_INLINE
void
_dispatch_once(dispatch_once_t *once, dispatch_block_t block)
{
dispatch_once_gate_t gate;
dispatch_once_callout_t callout;
uintptr_t pre;
uintptr_t post;
pre = OS_atomic_load_acq(once, ONCE_GATE_PRE_BITS);
if (!pre) {
pre = OS_atomic_cmpxchg_acq(once, ONCE_GATE_PRE_bits(1), 0);
if (!pre) {
callout = (dispatch_once_callout_t)_dispatch_once_callout_with_block(block, once);
gate = ONCE_GATE_POST_callout((uintptr_t)callout);
if (OS_atomic_cmpxchg_acq(once, ONCE_GATE_POST_bits(gate), ONCE_GATE_PRE_BITS(1)) != ONCE_GATE_PRE_BITS(1)) {
_dispatch_once_callout_cancel(callout);
return;
}
block();
post = OS_atomic_xchg_acq(once, gate);
_dispatch_once_callout_release(callout, post);
return;
}
}
//其他线程调用时,等待上面的callout完成。
while (pre != ONCE_GATE_POST_done) {
OSSpinLockLock(&lock);
post = OS_atomic_load_acq(once, ONCE_GATE_POST_BITS);
if (post == ONCE_GATE_POST_done) {
OSSpinLockUnlock(&lock);
return;
}
post = OS_atomic_cmpxchg_acq(once, ONCE_GATE_POST_wait(pre), post);
if (post != ONCE_GATE_POST_bits(pre)) {
OSSpinLockUnlock(&lock);
if (pre != post) {
continue;
}
if (post == ONCE_GATE_POST_done) {
return;
}
}
_dispatch_thread_sleep_noninterruptible(ONCE_GATE_SLEEP_TIME);
pre = OS_atomic_load_acq(once, ONCE_GATE_PRE_BITS);
OSSpinLockUnlock(&lock);
}
}
宏定义解析
为了更好的理解源码,我们需要对 dispatch_once 函数中涉及的宏定义进行解析:
- ONCE_GATE_PRE_BITS(x) :宏定义,表示 dispatch_once 的前置位字段(用于保存执行状态)。
- ONCE_GATE_POST_bits(x) :宏定义,表示 dispatch_once 的后置位字段(用于保存执行结果)。
- ONCE_GATE_POST_callout(x) :宏定义,用于将 callout 转换为后置位字段的值。
- ONCE_GATE_POST_wait(x) :宏定义,用于生成等待状态的后置位字段值。
- ONCE_GATE_POST_done :宏定义,表示任务已执行完成。
执行流程
- 检查执行状态: 通过原子加载操作获取 dispatch_once 结构的前置位字段,判断任务是否已执行。
- 设置执行标志: 如果任务未执行,通过原子交换操作设置前置位字段为 1,表明任务已开始执行。
- 创建执行回调: 创建一个执行回调,并将 dispatch_once 结构的地址作为参数传递。
- 设置后置位字段: 将执行回调转换为后置位字段的值,并通过原子交换操作将其设置到 dispatch_once 结构中。
- 执行任务: 执行执行回调,完成任务。
- 设置完成标志: 将后置位字段设置为 ONCE_GATE_POST_done,表示任务已完成。
- 释放资源: 释放执行回调的资源。
dispatch_semaphore:信号量的力量
dispatch_semaphore 是一种信号量,用于同步和控制多线程之间的资源访问。它允许您指定可同时访问资源的最大线程数,从而防止资源竞争和死锁。
源码解析
DISPATCH_ALWAYS_INLINE
void
_dispatch_semaphore_signal(dispatch_semaphore_t dsema)
{
uintptr_t state = OS_atomic_inc_acq(&dsema->dsema_state, relaxed);
if (state < 0) {
_dispatch_wakeup(dsema);
}
}
宏定义解析
- OS_atomic_inc_acq(x, relaxed) :宏定义,以松散语义原子地递增变量。
执行流程
- 递增状态: 使用原子递增操作递增信号量的状态(表示可访问资源的线程数)。
- 唤醒等待线程: 如果递增后的状态小于 0,表示有线程正在等待资源,则唤醒它们。
结语
dispatch_once 和 dispatch_semaphore 是 GCD 中两个至关重要的函数,它们使您能够控制多线程环境中的任务执行和资源访问。通过深入理解这些函数的源码,您可以更深入地了解 GCD 的工作原理,并编写出更强大、更可靠的多线程代码。
为了进一步提升文章的可读性,我加入了更多过渡词和短语,以增强文章的连贯性和流动性。此外,我还对代码段进行了排版,使其更易于阅读和理解。希望这篇文章对您有所帮助!