返回

揭秘 GCD 的核心:dispatch_semaphore 信号量

IOS

对于 iOS 和 macOS 开发者而言,GCD(Grand Central Dispatch)无疑是构建并发程序的基石。在深入研究 dispatch_queue 之后,我们踏入信号量(semaphore)的世界——dispatch_semaphore。信号量在 GCD 中扮演着至关重要的角色,因此了解其运作原理对于掌握 GCD 至关重要。

信号量的定义

信号量是一种同步机制,用于控制对共享资源的访问。它通过一个计数器来实现,该计数器表示资源的可用性。当计数器大于零时,资源可用;当计数器为零时,资源不可用。

dispatch_semaphore 的运作方式

dispatch_semaphore 是 GCD 提供的信号量实现。它通过以下函数来管理信号量:

  • dispatch_semaphore_create(long value):创建一个新的信号量,其中 value 指定初始计数器值。
  • dispatch_semaphore_signal(dispatch_semaphore_t semaphore):递增信号量的计数器。
  • dispatch_semaphore_wait(dispatch_semaphore_t semaphore, dispatch_time_t timeout):递减信号量的计数器,如果计数器为零,则阻塞线程直到计数器大于零或超时。

信号量在 GCD 中的应用

信号量在 GCD 中有着广泛的应用。最常见的一个应用是控制对共享资源(例如队列或数据结构)的访问。通过使用信号量,我们可以确保只有一个线程可以同时访问共享资源,从而避免并发问题。

另一个应用是同步多个线程的执行。例如,我们可以使用信号量来确保在执行特定任务之前,所有依赖任务都已完成。

理解信号量代码

为了进一步理解信号量的运作方式,让我们深入研究 dispatch_semaphore 的代码。在 GCD 的源代码中,dispatch_semaphore 定义如下:

struct dispatch_semaphore_s {
    DISPATCH_STRUCT_HEADER(dispatch_semaphore_s, _os_unfair_lock_s);
    uint64_t _value;
    long _waiters;
};

这个结构包含三个字段:

  • _value:信号量的计数器。
  • _os_unfair_lock_s:一个不公平锁,用于保护信号量免受并发访问。
  • _waiters:一个计数器,表示正在等待信号量递增的线程数。

信号量的使用示例

下面是一个使用信号量控制对共享资源访问的简单示例:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    // 在这里访问共享资源
    dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    // 在这里访问共享资源
    dispatch_semaphore_signal(semaphore);
});

在这个示例中,我们创建了一个初始计数器值为 1 的信号量。两个并行执行的线程都试图访问共享资源,但是由于信号量,它们只能依次访问共享资源。

总结

dispatch_semaphore 是 GCD 中的一个重要组件,用于同步并发线程并控制对共享资源的访问。通过理解其运作方式和应用,我们可以有效利用信号量来构建健壮、高效的并发程序。

扩展阅读

信号量维基百科

GCD 参考文档