揭秘GCD函数与死锁根源,探索并发编程的奥秘
2023-11-12 01:33:19
GCD 揭秘:同步与异步函数的机制
GCD 简介
Grand Central Dispatch (GCD) 是 Apple 提供的一个强大的并发框架,它允许开发者轻松地创建和管理并发任务。GCD 通过将任务分配给不同的队列来实现并发,从而使应用程序可以充分利用多核处理器的优势。
同步函数
同步函数在当前线程上阻塞,直到任务执行完毕。这意味着当前线程无法执行其他任务,直到同步任务完成。GCD 中常用的同步函数包括:
- dispatch_sync() :在当前线程上同步执行一个任务。
- dispatch_barrier_sync() :在当前线程上同步执行一个任务,并且在此期间阻止其他线程访问同一队列。
异步函数
异步函数在当前线程上不会阻塞。任务执行完毕后,GCD 会通过回调函数通知调用者。这意味着当前线程可以在任务执行期间继续执行其他任务。GCD 中常用的异步函数包括:
- dispatch_async() :在后台队列上异步执行一个任务。
- dispatch_apply() :并行执行一个任务集。
死锁的根源
死锁是指两个或多个线程相互等待,导致程序无法继续执行。在 GCD 中,死锁通常是由于不当使用同步函数造成的。例如,如果一个线程在串行队列中使用 dispatch_sync()
调用另一个线程中的任务,那么这两个线程就会发生死锁,因为第一个线程等待第二个线程完成任务,而第二个线程又等待第一个线程释放锁。
源码分析
为了更好地理解 GCD 的运行机制,我们可以分析其源码。 GCD 的源码位于 XNU 内核中,是一个庞大而复杂的系统。下面,我们重点分析与同步函数和异步函数相关的部分:
同步函数
static void _dispatch_sync_f_slow(dispatch_queue_t queue, dispatch_function_t func, void *ctxt)
{
dispatch_barrier_sync_f_slow(queue, func, ctxt);
}
dispatch_sync()
实际上调用了 dispatch_barrier_sync_f_slow()
。
static void _dispatch_barrier_sync_f_slow(dispatch_queue_t queue, dispatch_function_t func, void *ctxt)
{
dispatch_barrier_sync(queue, ^ {
func(ctxt);
});
}
dispatch_barrier_sync_f_slow()
将任务包装成一个 block,然后调用 dispatch_barrier_sync()
。
static void _dispatch_barrier_sync(dispatch_queue_t queue)
{
dispatch_sync(queue, ^{});
}
dispatch_barrier_sync()
实际上调用了 dispatch_sync()
。
static void _dispatch_sync(dispatch_queue_t queue, dispatch_function_t func, void *ctxt)
{
dispatch_assert_queue(queue);
_dispatch_thread_override_mach_thread_np(YES);
_dispatch_thread_override_mach_port_np(YES);
_dispatch_root_queue_push(queue, func, ctxt, NULL);
_dispatch_mach_thread_activity_v2();
_dispatch_thread_override_mach_thread_np(NO);
_dispatch_thread_override_mach_port_np(NO);
}
dispatch_sync()
首先检查当前线程是否是队列的线程,如果不是,则将其标记为队列的线程。然后,将任务包装成一个 block 并将其推入队列。接下来,它调用 _dispatch_mach_thread_activity_v2()
将当前线程标记为活动线程。最后,将当前线程标记为不是队列的线程。
异步函数
static void _dispatch_async_f_slow(dispatch_queue_t queue, dispatch_function_t func, void *ctxt)
{
dispatch_barrier_async_f_slow(queue, func, ctxt);
}
dispatch_async()
实际上调用了 dispatch_barrier_async_f_slow()
。
static void _dispatch_barrier_async_f_slow(dispatch_queue_t queue, dispatch_function_t func, void *ctxt)
{
dispatch_barrier_async(queue, ^ {
func(ctxt);
});
}
dispatch_barrier_async_f_slow()
将任务包装成一个 block,然后调用 dispatch_barrier_async()
。
static void _dispatch_barrier_async(dispatch_queue_t queue)
{
dispatch_async(queue, ^{});
}
dispatch_barrier_async()
实际上调用了 dispatch_async()
。
static void _dispatch_async(dispatch_queue_t queue, dispatch_function_t func, void *ctxt)
{
dispatch_assert_queue(queue);
_dispatch_root_queue_push(queue, func, ctxt, NULL);
}
dispatch_async()
首先检查当前线程是否是队列的线程,如果不是,则将任务包装成一个 block 并将其推入队列。
结论
通过分析 GCD 的源码,我们可以更深入地了解其运行机制,从而在实际开发中有效地使用 GCD。同时,我们应该避免不当使用同步函数,以防止死锁的发生。
常见问题解答
- 什么是 GCD?
GCD 是一个并发框架,用于轻松创建和管理并发任务。 - 同步函数和异步函数有什么区别?
同步函数在当前线程上阻塞,直到任务完成;异步函数不会阻塞,任务完成后通过回调函数通知调用者。 - 死锁是如何发生的?
死锁是由不当使用同步函数造成的,例如在串行队列中使用dispatch_sync()
调用另一个线程中的任务。 - 如何避免死锁?
尽量使用异步函数,并且谨慎使用同步函数。 - GCD 中常用的同步函数和异步函数有哪些?
同步函数:dispatch_sync()
、dispatch_barrier_sync()
; 异步函数:dispatch_async()
、dispatch_apply()
。