返回

iOS 源码解读:GCD 的信号量 semaphore

IOS

揭秘GCD信号量:多线程资源安全的基石

前言

在多线程编程中,资源竞争是一个棘手的问题。为了解决这个问题,GCD(Grand Central Dispatch)提供了信号量,这是一种高效且可靠的机制,用于保护共享资源的并发访问。本文将深入剖析GCD信号量的实现,从源码出发,揭示它的工作原理和优势。

信号量概述

信号量本质上是一个整型变量,代表着可用的资源数量。当一个线程需要访问受保护的资源时,它会调用dispatch_semaphore_wait操作,这会减少信号量。如果信号量大于0,线程可以立即访问资源。如果信号量小于0,线程将被阻塞,直到信号量再次变为正。

当线程完成对资源的访问时,它会调用dispatch_semaphore_signal操作,这会增加信号量。如果有一个或多个阻塞的线程正在等待该资源,其中一个线程将被唤醒。

semaphore源码解析

GCD信号量的实现位于libdispatch/semaphore.c文件中。semaphore结构如下:

struct semaphore {
    sem_t _sem; // Mach内核信号量
    uint64_t _xref; // 引用其他信号量的数量
    uint64_t _ref; // 引用该信号量的线程数量
    uint64_t _value; // 可用资源数量
    uint64_t _orig; // 信号量初始值
};
  • _sem:Mach内核信号量。
  • _xref:信号量引用的其他信号量数量。
  • _ref:引用该信号量的线程数量。
  • _value:可用的资源数量。
  • _orig:信号量初始值。

dispatch_semaphore_create函数

dispatch_semaphore_create函数用于创建一个信号量:

dispatch_semaphore_t dispatch_semaphore_create(long value) {
    struct semaphore *sem;

    sem = _dispatch_semaphore_create_with_value(value, 0);
    if (sem) {
        sem->_orig = value;
    }
    return (dispatch_semaphore_t)sem;
}

这个函数创建一个semaphore结构,并初始化它的值和初始值。

dispatch_semaphore_wait函数

dispatch_semaphore_wait函数用于使线程等待信号量变为正:

long dispatch_semaphore_wait(dispatch_semaphore_t _Nonnull sem, dispatch_time_t timeout) {
    long err;

    switch (_dispatch_semaphore_timedwait(sem, _dispatch_get_dispatch_time(timeout))) {
        case DISPATCH_OPERATION_TIMED_OUT:
            err = ETIMEDOUT;
            break;
        case DISPATCH_OPERATION_CANCELED:
            err = ECANCELED;
            break;
        case DISPATCH_OPERATION_OVERFLOW:
            err = EOVERFLOW;
            break;
        default:
            err = 0;
    }

    return (err);
}

这个函数调用_dispatch_semaphore_timedwait函数,等待信号量变为正。

dispatch_semaphore_signal函数

dispatch_semaphore_signal函数用于增加信号量并唤醒等待的线程:

void dispatch_semaphore_signal(dispatch_semaphore_t _Nonnull sem) {
    _dispatch_semaphore_signal(sem);
}

这个函数调用_dispatch_semaphore_signal函数,增加信号量并唤醒等待的线程。

总结

信号量是GCD中一个强大的工具,用于保护多线程中的共享资源。本文从源码的角度深入剖析了信号量的实现,帮助我们理解它的工作原理和优势。通过使用信号量,我们可以确保多线程程序的资源安全和高效执行。

常见问题解答

1. 信号量和互斥锁有什么区别?

信号量和互斥锁都是用于同步访问共享资源的机制,但它们的工作方式不同。互斥锁一次只允许一个线程访问资源,而信号量允许多个线程同时访问资源,只要可用的资源数量不超过信号量的值。

2. 什么时候应该使用信号量?

当需要限制对共享资源的并发访问次数时,应该使用信号量。例如,在数据库连接池中,可以使用信号量来限制同时可以连接到数据库的最大线程数量。

3. 信号量的初始值有什么限制?

信号量的初始值必须是非负整数。如果初始值小于0,信号量将被认为无效。

4. 如何处理信号量溢出?

GCD信号量使用64位整数来存储信号量值,这意味着它们不太可能溢出。但是,如果信号量溢出,它将被视为无效。

5. 信号量对性能有什么影响?

信号量的使用会对性能产生一些影响,因为它们需要操作系统内核的参与。但是,在大多数情况下,这种影响可以忽略不计。