iOS 源码解读:GCD 的信号量 semaphore
2024-02-16 04:08:24
揭秘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. 信号量对性能有什么影响?
信号量的使用会对性能产生一些影响,因为它们需要操作系统内核的参与。但是,在大多数情况下,这种影响可以忽略不计。