返回

GCD 函数的同步性、异步性、单例

IOS

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 函数的底层实现,我们可以深入了解它们的工作原理以及如何正确使用它们。同步函数用于在当前线程上执行任务,而异步函数用于在后台线程上执行任务。单例模式确保在应用程序中只有一个特定类的实例。了解这些概念对于编写高效且可维护的多线程代码至关重要。