lock - ShenYj/ShenYj.github.io GitHub Wiki
锁作为一种非强制的机制,被用来保证线程安全。每一个线程在访问数据或资源前,要先获取(Acquire)锁,并在访问结束之后释放(Release)锁。如果锁已经被占用,其他试图获取锁的线程会等待,知道锁重新可用
不应该将过多的操作代码放到锁里面,否则一个线程执行的时候另外一个线程一直在等待,将无法发挥多线程的作用,应该只将关键代码放入锁内
在 iOS 中锁的基本种类只有两种: 互斥锁
和 自旋锁
其他的比如 条件锁
、 递归锁
、 信号量
都是上层封装和实现
互斥锁
互斥锁(Mutual exclusion,缩写 Mutex)防止两条线程同时对同一公共资源进行读写的机制。当获取锁操作失败时,线程会进入睡眠,等待锁释放时被唤醒。
互斥锁又分为
-
递归锁
可重入锁,同一个线程在锁释放前可再次获取锁,即可以递归调用 -
非递归锁
不可重入,必须等锁释放后才能再次获取锁
自旋锁
线程反复检查锁变量是否可用。由于线程在这一过程中保持执行,因此是一种 忙等待
。一旦获取了自旋锁,线程会一直保持该锁,直至显示释放自旋锁
自旋锁避免了进程上下文的调度开销,因此对于线程只会阻塞很短时间的场合是有效的
互斥和自旋锁的区别
-
互斥锁
:
在线程获取锁但没有获取到时,线程会进入休眠状态,等锁被释放时线程会被唤醒 -
自旋锁
:
线程会一直处于等待状态(忙等待)不会进入休眠,因此效率高
- OSSpinLock
- os_unfair_lock
- pthread_mutex
- NSLock
- NSRecursiveLock
- NSCondition
- NSConditionLock
- dispatch_semaphore
- @synchronized
- atomic
- pthread_rwlock
- dispatch_queue
- dispatch_barrier_async
OSSpinLock
叫做自旋锁,等待锁的线程会处于忙等(busy-wait)状态,一直占用着 CPU资源
使用时需要导入头文件
#import <libkern/OSAtomic.h>
-
目前已经不再安全,可能出现优先级反转问题
如果等待锁的线程优先级较高,它会一直占用着 CPU 资源,优先级低的线程就无法释放锁
- 最早看到有关这方面描述的一篇文章: ibireme (郭曜源)的 不再安全的 OSSpinLock
具体来说,如果一个低优先级的线程获得锁并访问共享资源,这时一个高优先级的线程也尝试获得这个锁,它会处于 spin lock 的忙等状态从而占用大量 CPU。此时低优先级线程无法与高优先级线程争夺 CPU 时间,从而导致任务迟迟完不成、无法释放 lock。这并不只是理论上的问题,libobjc 已经遇到了很多次这个问题了,于是苹果的工程师停用了 OSSpinLock。
- 最早看到有关这方面描述的一篇文章: ibireme (郭曜源)的 不再安全的 OSSpinLock
-
api
// 初始化 OSSpinLock lock = OS_SPINLOCK_INIT; // 尝试加锁 OSSpinLockTry(&lock); // 加锁 OSSpinLockLock(&lock); // 解锁 OSSpinLockUnlock(&lock);
os_unfair_lock
用于取代不安全的 OSSpinLock
,从iOS10开始才支持
需要导入头文件
#import <os/lock.h>
-
api
// 初始化 os_unfair_lock lock = OS_UNFAIR_LOCK_INIT; // 尝试加锁 os_unfair_lock_trylock(&lock); // 加锁 os_unfair_lock_lock(&lock); // 解锁 os_unfair_lock_unlock(&lock);
从底层汇编调用看,等待 os_unfair_lock
锁的线程会处于休眠状态 (执行汇编 syscall
调用到内核api,与 pthread_mutex
流程一致,而不会像 OSSpinLock
进入到汇编的循环状态 ),并非忙等
苹果有关 os_unfair_lock_lock 的描述
This is a replacement for the deprecated OSSpinLock. This function doesn't spin on contention, but instead waits in the kernel to be awoken by an unlock. Like OSSpinLock, this function does not enforce fairness or lock ordering—for example, an unlocker could potentially reacquire the lock immediately, before an awoken waiter gets an opportunity to attempt to acquire the lock. This may be advantageous for performance reasons, but also makes starvation of waiters a possibility.
mutex
叫做 互斥锁
,等待锁的线程会处于休眠状态。是跨平台的一个锁方案,其他很多锁都是基于 pthread_mutex
的封装
需要导入头文件
#import <pthread.h>
-
api
/** * * Mutex type attributes * #define PTHREAD_MUTEX_NORMAL 0 * #define PTHREAD_MUTEX_ERRORCHECK 1 * #define PTHREAD_MUTEX_RECURSIVE 2 // 递归 * #define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL */ // 初始化属性 pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); // 初始化锁 pthread_mutex_init(mutex, &attr); // 初始化锁结束以后,销毁属性 pthread_mutexattr_destroy(&attr); // 加锁 pthread_mutex_lock(&_mutex); // 解锁 pthread_mutex_unlock(&_mutex); // 销毁锁 pthread_mutex_destroy(&_mutex);
可以不初始化属性,在传属性的时候直接传
NULL
,表示使用默认属性PTHREAD_MUTEX_NORMAL
。pthread_mutex_init(mutex, NULL);
pthread_mutex
,从设置属性枚举值得知,即能实现非递归锁,也能实现递归锁,同时还支持条件锁
-
设置条件的 api
// 初始化条件 pthread_cond_t condition pthread_cond_init(&_cond, NULL); // 等待条件 pthread_cond_wait(&_cond, &_mutex); //激活一个等待该条件的线程 pthread_cond_signal(&_cond); //激活所有等待该条件的线程 pthread_cond_broadcast(&_cond); // 销毁条件 pthread_cond_destroy(&_cond);
NSLock
是对 pthread_mutex
普通锁的封装。pthread_mutex_init(mutex, NULL);
NSLock
遵循 NSLocking
协议。
-
Lock
方法是加锁 -
unlock
是解锁 -
tryLock
是尝试加锁,如果失败的话返回NO
-
lockBeforeDate:
是在指定Date
之前尝试加锁,如果在指定时间之前都不能加锁,则返回NO
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
@interface NSLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name
@end
从 NSLock
源码定义来看还是比较简单的,转换成 C++
后也只能看到一些消息发送
NSLock *lock = ((NSLock *(*)(id, SEL))(void *)objc_msgSend)((id)((NSLock *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSLock"), sel_registerName("alloc")), sel_registerName("init"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)lock, sel_registerName("lock"));
((BOOL (*)(id, SEL))(void *)objc_msgSend)((id)lock, sel_registerName("tryLock"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)lock, sel_registerName("unlock"));
翻阅了下 GNUStep
代码作为参考, 里面找到了一些关于 NSLock
的实现 (GNU 并不是苹果的,这里只是将一些苹果未开源的部分模拟实现了)
NSLock (GNU)
@implementation NSLock
+ (id) allocWithZone: (NSZone*)z
{
if (self == baseLockClass && YES == traceLocks)
{
return class_createInstance(tracedLockClass, 0);
}
return class_createInstance(self, 0);
}
+ (void) initialize
{
static BOOL beenHere = NO;
if (beenHere == NO)
{
beenHere = YES;
/* Initialise attributes for the different types of mutex.
* We do it once, since attributes can be shared between multiple
* mutexes.
* If we had a pthread_mutexattr_t instance for each mutex, we would
* either have to store it as an ivar of our NSLock (or similar), or
* we would potentially leak instances as we couldn't destroy them
* when destroying the NSLock. I don't know if any implementation
* of pthreads actually allocates memory when you call the
* pthread_mutexattr_init function, but they are allowed to do so
* (and deallocate the memory in pthread_mutexattr_destroy).
*/
pthread_mutexattr_init(&attr_normal);
pthread_mutexattr_settype(&attr_normal, PTHREAD_MUTEX_NORMAL);
pthread_mutexattr_init(&attr_reporting);
pthread_mutexattr_settype(&attr_reporting, PTHREAD_MUTEX_ERRORCHECK);
pthread_mutexattr_init(&attr_recursive);
pthread_mutexattr_settype(&attr_recursive, PTHREAD_MUTEX_RECURSIVE);
/* To emulate OSX behavior, we need to be able both to detect deadlocks
* (so we can log them), and also hang the thread when one occurs.
* the simple way to do that is to set up a locked mutex we can
* force a deadlock on.
*/
pthread_mutex_init(&deadlock, &attr_normal);
pthread_mutex_lock(&deadlock);
baseConditionClass = [NSCondition class];
baseConditionLockClass = [NSConditionLock class];
baseLockClass = [NSLock class];
baseRecursiveLockClass = [NSRecursiveLock class];
tracedConditionClass = [GSTracedCondition class];
tracedConditionLockClass = [GSTracedConditionLock class];
tracedLockClass = [GSTracedLock class];
tracedRecursiveLockClass = [GSTracedRecursiveLock class];
untracedConditionClass = [GSUntracedCondition class];
untracedConditionLockClass = [GSUntracedConditionLock class];
untracedLockClass = [GSUntracedLock class];
untracedRecursiveLockClass = [GSUntracedRecursiveLock class];
}
}
MDEALLOC
MDESCRIPTION
MFINALIZE
/* Use an error-checking lock. This is marginally slower, but lets us throw
* exceptions when incorrect locking occurs.
*/
- (id) init
{
if (nil != (self = [super init]))
{
if (0 != pthread_mutex_init(&_mutex, &attr_reporting))
{
DESTROY(self);
}
}
return self;
}
MISLOCKED
MLOCK
- (BOOL) lockBeforeDate: (NSDate*)limit
{
do
{
int err = pthread_mutex_trylock(&_mutex);
if (0 == err)
{
CHK(Hold)
return YES;
}
if (EDEADLK == err)
{
(*_NSLock_error_handler)(self, _cmd, NO, @"deadlock");
}
sched_yield();
} while ([limit timeIntervalSinceNow] > 0);
return NO;
}
MNAME
MSTACK
MTRYLOCK
MUNLOCK
@end
NSRecursiveLock
是对 pthread_mutex
递归锁的封装,API 跟 NSLock
基本一致
NSRecursiveLock (GNU)
@implementation NSRecursiveLock
+ (id) allocWithZone: (NSZone*)z
{
if (self == baseRecursiveLockClass && YES == traceLocks)
{
return class_createInstance(tracedRecursiveLockClass, 0);
}
return class_createInstance(self, 0);
}
+ (void) initialize
{
[NSLock class]; // Ensure mutex attributes are set up.
}
MDEALLOC
MDESCRIPTION
MFINALIZE
- (id) init
{
if (nil != (self = [super init]))
{
if (0 != pthread_mutex_init(&_mutex, &attr_recursive))
{
DESTROY(self);
}
}
return self;
}
MISLOCKED
MLOCK
MLOCKBEFOREDATE
MNAME
MSTACK
MTRYLOCK
MUNLOCK
@end
NSCondition
是对 pthread_mutex
和 cond
的封装
NSCondition (GNU)
@implementation NSCondition
+ (id) allocWithZone: (NSZone*)z
{
if (self == baseConditionClass && YES == traceLocks)
{
return class_createInstance(tracedConditionClass, 0);
}
return class_createInstance(self, 0);
}
+ (void) initialize
{
[NSLock class]; // Ensure mutex attributes are set up.
}
- (void) broadcast
{
pthread_cond_broadcast(&_condition);
}
MDEALLOC
MDESCRIPTION
- (void) finalize
{
pthread_cond_destroy(&_condition);
pthread_mutex_destroy(&_mutex);
}
- (id) init
{
if (nil != (self = [super init]))
{
if (0 != pthread_cond_init(&_condition, NULL))
{
DESTROY(self);
}
else if (0 != pthread_mutex_init(&_mutex, &attr_reporting))
{
pthread_cond_destroy(&_condition);
DESTROY(self);
}
}
return self;
}
MISLOCKED
MLOCK
MLOCKBEFOREDATE
MNAME
- (void) signal
{
pthread_cond_signal(&_condition);
}
MSTACK
MTRYLOCK
MUNLOCK
- (void) wait
{
pthread_cond_wait(&_condition, &_mutex);
}
- (BOOL) waitUntilDate: (NSDate*)limit
{
NSTimeInterval ti = [limit timeIntervalSince1970];
double secs, subsecs;
struct timespec timeout;
int retVal = 0;
// Split the float into seconds and fractions of a second
subsecs = modf(ti, &secs);
timeout.tv_sec = secs;
// Convert fractions of a second to nanoseconds
timeout.tv_nsec = subsecs * 1e9;
/* NB. On timeout the lock is still held even through condition is not met
*/
retVal = pthread_cond_timedwait(&_condition, &_mutex, &timeout);
if (retVal == 0)
{
return YES;
}
if (retVal == ETIMEDOUT)
{
return NO;
}
if (retVal == EINVAL)
{
NSLog(@"Invalid arguments to pthread_cond_timedwait");
}
return NO;
}
@end
NSConditionLock
是对 NSCondition
的进一步封装,可以设置具体的条件值
-
NSConditionLock
定义@interface NSConditionLock : NSObject <NSLocking> { - (instancetype)initWithCondition:(NSInteger)condition; @property (readonly) NSInteger condition; - (void)lockWhenCondition:(NSInteger)condition; - (BOOL)tryLock; - (BOOL)tryLockWhenCondition:(NSInteger)condition; - (void)unlockWithCondition:(NSInteger)condition; - (BOOL)lockBeforeDate:(NSDate *)limit; - (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit; @property (nullable, copy) NSString *name; @end
-
NSConditionLock
提供的关键 api-
initWithCondition:(NSInteger)condition
初始化 condition,并且设置状态值 -
lockWhenCondition:(NSInteger)condition
当状态值为 condition 的时候加锁 -
unlockWithCondition:(NSInteger)condition
当状态值为 condition 的时候解锁
-
对比简单的 wait
和 signal
方法,允许给条件设置值
NSConditionLock (GNU)
@implementation NSConditionLock
+ (id) allocWithZone: (NSZone*)z
{
if (self == baseConditionLockClass && YES == traceLocks)
{
return class_createInstance(tracedConditionLockClass, 0);
}
return class_createInstance(self, 0);
}
+ (void) initialize
{
[NSLock class]; // Ensure mutex attributes are set up.
}
- (NSInteger) condition
{
return _condition_value;
}
- (void) dealloc
{
[_name release];
[_condition release];
[super dealloc];
}
- (id) init
{
return [self initWithCondition: 0];
}
- (id) initWithCondition: (NSInteger)value
{
if (nil != (self = [super init]))
{
if (nil == (_condition = [NSCondition new]))
{
DESTROY(self);
}
else
{
_condition_value = value;
[_condition setName:
[NSString stringWithFormat: @"condition-for-lock-%p", self]];
}
}
return self;
}
- (BOOL) isLockedByCurrentThread
{
return [_condition isLockedByCurrentThread];
}
- (void) lock
{
[_condition lock];
}
- (BOOL) lockBeforeDate: (NSDate*)limit
{
return [_condition lockBeforeDate: limit];
}
- (void) lockWhenCondition: (NSInteger)value
{
[_condition lock];
while (value != _condition_value)
{
[_condition wait];
}
}
- (BOOL) lockWhenCondition: (NSInteger)condition_to_meet
beforeDate: (NSDate*)limitDate
{
if (NO == [_condition lockBeforeDate: limitDate])
{
return NO; // Not locked
}
if (condition_to_meet == _condition_value)
{
return YES; // Keeping the lock
}
while ([_condition waitUntilDate: limitDate])
{
if (condition_to_meet == _condition_value)
{
return YES; // Keeping the lock
}
}
[_condition unlock];
return NO; // Not locked
}
MNAME
MSTACK
- (BOOL) tryLock
{
return [_condition tryLock];
}
- (BOOL) tryLockWhenCondition: (NSInteger)condition_to_meet
{
if ([_condition tryLock])
{
if (condition_to_meet == _condition_value)
{
return YES; // KEEP THE LOCK
}
else
{
[_condition unlock];
}
}
return NO;
}
- (void) unlock
{
[_condition unlock];
}
- (void) unlockWithCondition: (NSInteger)value
{
_condition_value = value;
[_condition broadcast];
[_condition unlock];
}
@end
semaphore
叫做 信号量
信号量的初始值,可以用来控制线程并发访问的最大数量
e.g. 信号量的初始值为
1
,代表同时只允许1条线程访问资源,保证线程同步
-
e.g.
// 最多开启 5个 线程 dispatch_semaphore_create(5); // 如果信号量的值 > 0,就让信号量的值减1,然后继续往下执行代码 // 如果信号量的值 <= 0,就会休眠等待,直到信号量的值变成 >0,就让信号量的值 减1,然后继续往下执行代码 dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER); // 让信号量的值 +1 dispatch_semaphore_signal(self.semaphore);
@synchronized
也是对 pthread_mutex
递归锁的封装,@synchronized(obj)
内部会生成 obj
对应的递归锁,然后进行加锁、解锁操作
是一种最简单又十分安全的锁方案,但是性能比较差,所在 Xcode 中连智能提示都没有,并不推荐使用
作为锁对象的两个条件:
- 继承自NSObject
- 必须是全局的
如果不满足全局,就不能保证锁的唯一性,相当于入口不唯一:
当一条线程访问公共资源前进行了加锁, 另外一条线程访问公共资源前也加了锁, 两个线程从不同的入口进入, 获取到了公共资源, 这样也就失去了意义
之所以经常配合 self 使用, 是因为 self 是最容易获取到的2个条件都满足的全局锁对象
@synchronized的原理
- 默认继承自
NSObject
的对象内部(SyncData
)都有一把互斥锁, 默认开启状态早期版本
recursive_mutex_tt
中包装的mLock
是基于pthread_mutex
递归锁的封装,目前源码objc-781
上改为了os_unfair_recursive_lock
- 当执行到
@synchronized
关键字时,会先检查对象的锁的状态是开启还是关闭, 通过锁对象内部的锁, 锁住大括号内的代码,再去执行大括号内的代码, 这样其他线程就被挡在锁外等候, 当进入互斥锁内部的线程执行完锁内代码后, 会重新打开互斥锁, 这样在锁外等候的线程就可以进来了,重复加锁-->执行内部代码-->解锁的操作
- 最终会通过调用
objc_sync_enter
和objc_sync_exit
两个方法进行加锁、解锁操作
@synchronized
的实现原理比这里描述的要复杂的多,@synchronized
维护了一个 hash表
,hash表
中保存了每条线程使用 @synchronized
的情况,用 threadCount
记录线程数,用 lockCount
记录每个线程下的加锁数量。所以 @synchronized
才能支持多线程和嵌套使用。
@synchronized - objc-781中的源码
using recursive_mutex_t = recursive_mutex_tt<LOCKDEBUG>;
class recursive_mutex_tt : nocopy_t {
os_unfair_recursive_lock mLock;
省略 .....
}
typedef struct os_unfair_recursive_lock_s {
os_unfair_lock ourl_lock;
uint32_t ourl_count;
} os_unfair_recursive_lock, *os_unfair_recursive_lock_t;
typedef struct alignas(CacheLineSize) SyncData {
struct SyncData* nextData;
DisguisedPtr<objc_object> object;
int32_t threadCount; // number of THREADS using this block
recursive_mutex_t mutex;
} SyncData;
// Begin synchronizing on 'obj'.
// Allocates recursive mutex associated with 'obj' if needed.
// Returns OBJC_SYNC_SUCCESS once lock is acquired.
int objc_sync_enter(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
SyncData* data = id2data(obj, ACQUIRE);
ASSERT(data);
data->mutex.lock();
} else {
// @synchronized(nil) does nothing
if (DebugNilSync) {
_objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
}
objc_sync_nil();
}
return result;
}
BOOL objc_sync_try_enter(id obj)
{
BOOL result = YES;
if (obj) {
SyncData* data = id2data(obj, ACQUIRE);
ASSERT(data);
result = data->mutex.tryLock();
} else {
// @synchronized(nil) does nothing
if (DebugNilSync) {
_objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
}
objc_sync_nil();
}
return result;
}
-
原子性
原子性是指一个操作是不可中断的,要么全部执行成功要么全部执行失败,有着“同生共死”的感觉。及时在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程所干扰。
atomic
用于保证属性 setter
、 getter
的原子性操作,通过在 getter
和 setter
内部加了线程同步的锁来实现原子性, 它并不能保证使用属性的过程是线程安全的
比如一个可变数组 NSMutableArray
实例,通过 addObject:
方法添加元素时并不是线程安全的
-
参考
objc4-781
和objc4-818
中objc-accessors.mm
内属性set
方法的具体实现static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) { if (offset == 0) { object_setClass(self, newValue); return; } id oldValue; id *slot = (id*) ((char*)self + offset); if (copy) { newValue = [newValue copyWithZone:nil]; } else if (mutableCopy) { newValue = [newValue mutableCopyWithZone:nil]; } else { if (*slot == newValue) return; newValue = objc_retain(newValue); } if (!atomic) { oldValue = *slot; *slot = newValue; } else { spinlock_t& slotlock = PropertyLocks[slot]; slotlock.lock(); oldValue = *slot; *slot = newValue; slotlock.unlock(); } objc_release(oldValue); } void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) { bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY); bool mutableCopy = (shouldCopy == MUTABLE_COPY); reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy); }
根据设置的原子性,取决于是否加锁
using spinlock_t = mutex_tt<LOCKDEBUG>; class mutex_tt : nocopy_t { os_unfair_lock mLock; ... }
从结构、成员来看,在
781
下也是个互斥锁
与 pthread_mutex
一样同属于 pthread
,由 POSIX
标准提供的接口,但是苹果似乎并未开源提供给开发者使用, 在 GNU
源码中能搜到部分接口信息
pthread_rwlock
读写锁,经常用于文件等数据的读写操作,需要导入头文件 #import <pthread.h>
读写锁
实际是一种特殊的自旋锁
,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。这种锁相对于自旋锁而言,能提高并发性,因为在多处理器系统中,它允许同时有多个读者来访问共享资源,最大可能的读者数为实际的CPU数写者是排他性的,⼀个读写锁同时只能有⼀个写者或多个读者(与CPU数相关),但不能同时既有读者⼜有写者。在读写锁保持期间也是抢占失效的 如果读写锁当前没有读者,也没有写者,那么写者可以⽴刻获得读写锁,否则它必须⾃旋在那⾥,直到没有任何写者或读者。如果读写锁没有写者,那么读者可以⽴即获得该读写锁,否则读者必须⾃旋在那⾥,直到写者释放该读写锁
-
关键 api
// 导入头文件 #import <pthread.h> // 全局声明读写锁 pthread_rwlock_t lock; // 初始化读写锁 pthread_rwlock_init(&lock, NULL); // 读操作-加锁 pthread_rwlock_rdlock(&lock); // 读操作-尝试加锁 pthread_rwlock_tryrdlock(&lock); // 写操作-加锁 pthread_rwlock_wrlock(&lock); // 写操作-尝试加锁 pthread_rwlock_trywrlock(&lock); // 解锁 pthread_rwlock_unlock(&lock); // 释放锁 pthread_rwlock_destroy(&lock);
栅栏函数,只有配合自己创建的并发队列才有意义。
如果传入的是串行队列或全局队列,效果等同于 dispatch_async
函数
当前面的任务都执行完后,当前任务才会开始执行,当前任务执行完后,后面的任务才会开始执行。可以达到读写锁的效果。
利用 GCD 串行队列,直接可以解决访问公共资源的安全问题,因为串行本就是按顺序执行
-
由高到低排序
- OSSpinLock (已经被废弃)
- os_unfair_lock (iOS 10.0)
- dispatch_semaphore
- pthread_mutex
- dispatch_queue(DISPATCH_QUEUE_SERIAL)
- NSLock
- NSCondition
- pthread_mutex(recursive)
- NSRecursiveLock
- NSConditionLock
- @synchronized
-
结论
- 尽量不要使用递归锁(取决于业务)
- 互斥锁用于临界区持锁时间比较长的操作,自旋锁就主要用在临界区持锁时间非常短且CPU资源不紧张的情况下,自旋锁一般用于多核的服务器
iOS 这么多的同步方案,我使用最多的就是 GCD 和 信号量了,其他的在开源库中有接触,但基本都没有首选使用,在了解其内部实现原理后,可以结合今后业务的需求进行选择