主要讲解设计多读单写模型pthread_rwlock_t/dispatch_barrier_async的基本用法
常见锁的分类:
- 自旋锁
OSSpinLock
- 互斥锁
os_unfair_lock
- 互斥/递归/条件锁
pthread_mutex_t
- 互斥锁
NSLock
- 递归锁
NSRecursiveLock
- 条件锁
NSCondition
- 条件锁
NSConditionLock
- 递归锁
@synchronized
- 信号量
semaphore
- 读写锁
pthread_rwlock_t
- 异步栅栏
dispatch_barrier_async
iOS 锁 部分一
iOS 锁 部分二
iOS 锁 部分三
iOS 锁 部分四
1. 如果实现一个多读单写的模型
如何实现一个多读单写的模型,需求如下
- 同时可以有多个线程读取;
- 同时只能有一个线程写入;
- 同时只能执行读取或者写入的一种;
方案1 读写锁pthrad_rwlock_t
特点
- 读取加锁可以同时多个线程进行,写入同时只能一个线程进行, 等待的线程处于休眠状态;
- 可能会用到的方法
2.1pthread_rwlock_init()
初始化一个读写锁;
2.2pthread_rwlock_rdlock()
读写锁的读取加锁;
2.3pthread_rwlock_wrlock()
读写锁的写入加锁;
2.4pthread_rwlock_unlock()
解锁;
2.5pthread_rwlock_destroy()
销毁锁; - 测试代码如下, 测试代码主要看的是: 打印读取可以同时出现几个, 打印写入同时只会出现一个;
/***********************************pthread_rwlock_t*************************************/
- (void)rwLockType {
pthread_rwlock_init(&_lock, NULL);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
__weak typeof(self) weakSelf = self;
for (int i = 0; i < 100; i ++) {
///同时创建多个线程进行写入操作
dispatch_async(queue, ^{
[weakSelf lockWriteAction];
});
dispatch_async(queue, ^{
[weakSelf lockWriteAction];
});
dispatch_async(queue, ^{
[weakSelf lockWriteAction];
});
///同时创建多个线程进行读操作
dispatch_async(queue, ^{
[weakSelf lockReadAction];
});
dispatch_async(queue, ^{
[weakSelf lockReadAction];
});
dispatch_async(queue, ^{
[weakSelf lockReadAction];
});
}
}
- (void)lockReadAction {
pthread_rwlock_rdlock(&_lock);
sleep(1);
NSLog(@"RWLock Read Action %@", [NSThread currentThread]);
pthread_rwlock_unlock(&_lock);
}
- (void)lockWriteAction {
pthread_rwlock_wrlock(&_lock);
sleep(1);
NSLog(@"RWLock Write Action %@", [NSThread currentThread]);
pthread_rwlock_unlock(&_lock);
}
- (void)dealloc {
pthread_rwlock_destroy(&_lock);
}
/***********************************pthread_rwlock_t*************************************/
方案2 异步栅栏 dispath_barrier_async
特点
- 传入的
并发队列
队列必须是手动创建,dispatch_queue_create()
方式;
如果传入串行队列或者通过dispatch_get_global_queue()
方式创建, 则dispath_barrier_async
的作用就跟dispath_async
变得一样; - 可能会用到的方法
2.1dispatch_queue_create()
创建并发队列;
2.2dispatch_barrier_async()
异步栅栏; - 测试代码如下, 测试代码主要看的是: 打印读取可以同时出现几个, 打印写入同时只会出现一个;
/*********************************dispatch_barrier_async**********************************/
- (void)barrierAsyncType {
self.queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 100; i ++) {
///同时创建多个线程进行写入操作
[self barrierWriteAction];
[self barrierWriteAction];
[self barrierWriteAction];
///同时创建多个线程进行读取操作
[self barrierReadAction];
[self barrierReadAction];
[self barrierReadAction];
}
}
- (void)barrierReadAction {
dispatch_async(self.queue, ^{
sleep(1);
NSLog(@"barrier Read Action %@", [NSThread currentThread]);
});
}
- (void)barrierWriteAction {
dispatch_barrier_async(self.queue, ^{
sleep(1);
NSLog(@"barrier Write Action %@", [NSThread currentThread]);
});
}
/*********************************dispatch_barrier_async**********************************/
关于锁定的一些总结
1. 常用的锁的效率排序
-
os_unfair_lock
(iOS10
之后) -
OSSpinLock
(iOS10
之前) -
dispatch_semaphore
(iOS
版本兼容性好) -
pthread_mutex_t
(iOS
版本兼容行好) -
NSLock
( 基于pthread_mutex_t
封装) -
NSCondition
( 基于pthread_mutex_t
封装) -
pthread_mutex_t(recursive)
(递归锁的优先推荐) -
NSRecursiveLock
(基于pthread_mutex_t
封装) -
NSConditionLock
(基于NSCondition
封装) -
@synchronized
10.1iOS12
之前基于pthread_mutex_t
封装
10.2iOS12
之后基于os_unfair_lock
封装(iOS12
之后它的效率应该不是最低, 应该在3/4左右);
2. 自旋锁和互斥锁的取舍
自旋锁和互斥锁怎么选择, 其实这个问题已经没有什么意义, 因为自旋锁OSSpinLock
在iOS10
之后已经废弃, 而它的替换方案os_unfair_lock
是互斥锁;但是我们仍然做一下对比;
自旋锁:
- 预计线程需要等待的时间较短;
- 多核处理器;
-
CPU
的资源不紧张;
互斥锁:
- 预计线程需要等待的时间较长;
- 单核处理器;
- 临界区(加锁解锁之间部分)有
IO
操作; - 临界区的较为复杂和循环量比较大;
3. 其他注意点
3.1 加锁和解锁的实现一定要配套出现, 不然就会出现死锁的现象;
网友评论