GCD 函数的同步性、异步性、单例
2023-12-04 04:24:30
iOS 底层探索:GCD 多线程分析(八)—— 函数同步性、异步性、单例
在上篇文章中,我们探讨了 GCD 的同步函数如何导致死锁。在本文中,我们将更深入地探讨 GCD 函数的同步性和异步性,以及单例模式在 GCD 中的实现。
同步函数
同步函数会在当前线程上执行,直到完成为止。这意味着调用线程将被阻塞,直到同步函数返回。GCD 提供了几个同步函数,包括:
- dispatch_sync()
- dispatch_barrier_sync()
- dispatch_semaphore_wait()
异步函数
异步函数会在后台线程上执行,不会阻塞调用线程。GCD 提供了几个异步函数,包括:
- dispatch_async()
- dispatch_apply()
- dispatch_group_async()
单例模式
单例模式确保在应用程序中只有一个特定类的实例。GCD 使用一次性令牌来实现单例。当您创建单例时,GCD 会创建一个令牌,该令牌将用于 subsequent 分派。如果存在具有相同令牌的先前分派,则新的分派将被取消。
GCD 函数的源码分析
为了更好地理解 GCD 函数的同步性、异步性以及单例模式,让我们分析一下它们的底层实现。
dispatch_sync()
void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block) {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(queue, ^{
block();
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
正如你所看到的,dispatch_sync() 创建了一个信号量并将其传递给异步块。异步块在后台线程上执行,但调用线程会被阻塞,直到信号量被触发。这确保了同步函数在当前线程上完成。
dispatch_async()
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block) {
dispatch_queue_t target_queue = queue ?: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_block_t async_block = ^{
@autoreleasepool {
block();
}
};
dispatch_async_f(target_queue, async_block, NULL);
}
dispatch_async() 将块分派到指定队列或全局队列。异步块使用autoreleasepool,以便在块完成时自动释放任何分配的内存。
dispatch_once()
void dispatch_once(dispatch_once_t *once, dispatch_block_t block) {
dispatch_once_f(once, block, NULL);
}
dispatch_once() 使用 dispatch_once_t 结构来实现单例模式。dispatch_once_f() 函数将块分派到串行队列,并使用锁来确保块只执行一次。
结论
通过分析 GCD 函数的底层实现,我们可以深入了解它们的工作原理以及如何正确使用它们。同步函数用于在当前线程上执行任务,而异步函数用于在后台线程上执行任务。单例模式确保在应用程序中只有一个特定类的实例。了解这些概念对于编写高效且可维护的多线程代码至关重要。