iOS - 多线程的锁
Posted 极客学伟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS - 多线程的锁相关的知识,希望对你有一定的参考价值。
ios - 多线程的锁
多线程同时访问同一块资源会造成资源抢夺,容易引发数据错乱和数据安全问题,此时我们需要保证资源同时只有一个线程访问,加锁就是为了解决这个问题。
常用的加锁方式:(性能由差到好)
OSSpinLock
自旋锁,存在优先级反转问题,破坏了 spinLock,后来Apple推出os_unfair_lock_t
解决优先级翻转问题dispatch_semaphore
信号量pthread_mutex
互斥锁 (C语言)NSLock
对象锁NSCondition
条件锁NSRecursiveLock
递归锁NSConditionLock
条件锁@synchronized
性能最差
各种锁的性能比较
自旋锁
‘OSSpinLock’、‘os_unfair_lock’
‘OSSpinLock’ 是一种自旋锁,和互斥锁类似,都是为了保证线程安全的锁。对于互斥锁,当一个线程获得这个锁以后,其他想要获得此锁的线程将会被阻塞,直到该锁被释放;自旋锁,当一个线程获得锁之后,其他线程将会一直循环查看该锁是否被释放,此锁适用于锁的持有者保存时间较短的情况。
spinLock = OS_SPINLOCK_INIT;
OSSpinLockLock(&spinLock);
sleep(4);
OSSpinLockUnlock(&spinLock);
‘OSSpinLock’ 不再安全了? 因为存在优先级翻转的问题。
在新版本iOS系统中,系统维护了5个不同的线程优先级(Qos): background, utility, default, user-initiated, user-interactive。高优先级线程始终会在低优先级线程前执行,一个线程不会受到比他更低优先级线程的干扰。这种线程调度算法会产生潜在的优先级翻转的问题,从而破坏 spin lock.
实例:如果一个低优先级的线程获得锁并访问共享资源,此时一个高优先级的线程也尝试获得这个锁,它会处于 spin lock 的忙时状态从而占用大量CPU,此时低优先级线程无法与高优先级线程抢夺CPU时间,从而导致任务迟迟无法完成,无法释放lock。
为解决此问题,在iOS10,给出了新API:os_unfair_lock
os_unfair_lock_t unfairLock = &(OS_UNFAIR_LOCK_INIT);
os_unfair_lock_lock(unfairLock);
sleep(4);
os_unfair_lock_unlock(unfairLock);
信号量
dispatch_semaphore
信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量
dispatch_semaphore
可以通过控制并发数实现锁机制。通过控制信号量的值为 0 或 1 来实现锁。dispatch_semaphore_wait
会使信号量-1,dispatch_semaphore_signal
会使信号量+1,信号量为0时线程等待,为1时方可继续执行。
#import "Dispatch_Semaphore.h"
@interface Dispatch_Semaphore ()
dispatch_semaphore_t _semaphore;
@property (nonatomic, strong) NSMutableArray *array;
@property (assign) NSUInteger index;
@end
@implementation Dispatch_Semaphore
- (instancetype)init
self = [super init];
if (self)
/// 通道默认为1, 首次执行
_semaphore = dispatch_semaphore_create(1);
return self;
- (void)run
for (NSUInteger i = 0; i < self.array.count; i++)
if (i % 2 == 0)
dispatch_async(dispatch_get_global_queue(0, 0), ^
[self log];
);
else
dispatch_async(dispatch_get_global_queue(0, 0), ^
[self log];
);
- (void)log
/// 通道 -1 阻塞其他线程进入
dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"%@",self.array[self.index++]);
/// 通道+1 其他线程可进入
dispatch_semaphore_signal(_semaphore);
- (NSMutableArray *)array
if(!_array)
NSMutableArray *arrayM = [NSMutableArray array];
for (NSUInteger i = 0; i < 1000; i++)
[arrayM addObject:@(i)];
_array = arrayM;
return _array;
@end
互斥锁
pthread_mutex
#import "Phread_mutex.h"
#import <pthread.h>
@interface Phread_mutex ()
pthread_mutex_t _mutex_t;
@property (nonatomic, strong) NSMutableArray *array;
@property (assign) NSUInteger index;
@end
@implementation Phread_mutex
- (instancetype)init
self = [super init];
if (self)
/// 锁初始化
pthread_mutex_t mutex_t = PTHREAD_MUTEX_INITIALIZER;
_mutex_t = mutex_t;
return self;
- (void)run
for (NSUInteger i = 0; i < self.array.count; i++)
if (i % 2 == 0)
dispatch_async(dispatch_get_global_queue(0, 0), ^
[self log];
);
else
dispatch_async(dispatch_get_global_queue(0, 0), ^
[self log];
);
- (void)log
// 加互斥锁
pthread_mutex_lock(&(_mutex_t));
NSLog(@"%@",self.array[self.index++]);
// 解互斥锁
pthread_mutex_unlock(&(_mutex_t));
- (NSMutableArray *)array
if(!_array)
NSMutableArray *arrayM = [NSMutableArray array];
for (NSUInteger i = 0; i < 1000; i++)
[arrayM addObject:@(i)];
_array = arrayM;
return _array;
@end
NSLock
NSLock
和 NSRecursiveLock
, NSConditionLock
都是对 pthread_mutex
的封装。
NSLock 遵循 NSLocking 协议. lock 加锁,unlock 解锁,tryLock 尝试加锁,如果失败的话返回 NO,lockBeforeDate: 是在指定Date之前尝试加锁,如果在指定时间之前都不能加锁,则返回NO。
NSLocking
协议
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
- (void)log
// 加互斥锁
[self.lock lock];
NSLog(@"%@",self.array[self.index++]);
// 解互斥锁
[self.lock unlock];
条件锁
NSConditionLock
NSConditionLock
同样遵循了NSLocking
协议,除此之外,还可以设置自定义条件来获得锁和释放锁.
#import "NSConditionLockDemo.h"
@interface NSConditionLockDemo ()
@property (nonatomic, strong) NSMutableArray *images;
@property (assign) NSUInteger index;
@property (nonatomic, strong) NSConditionLock *conditionLock;
@end
@implementation NSConditionLockDemo
static const NSInteger lockTag = 10086;
static const NSInteger imageCount = 100;
- (instancetype)init
self = [super init];
if (self)
self.conditionLock = [[NSConditionLock alloc] init];
return self;
- (void)run
for (NSUInteger i = 0; i < imageCount; i++)
dispatch_async(dispatch_get_global_queue(0, 0), ^
[self downloadImages];
);
[self showImages];
/// 异步下载 imageCount 张图片
- (void)downloadImages
sleep(arc4random_uniform(5));
[self.conditionLock lock];
[self.images addObject:@(arc4random_uniform(100))];
[self.conditionLock unlock];
if (self.images.count == imageCount)
[self.conditionLock unlockWithCondition:lockTag];
/// 展示下载好的 imageCount 张图片
- (void)showImages
[self.conditionLock lockWhenCondition:lockTag];
[self.images enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop)
NSLog(@"show: %@",obj);
];
[self.conditionLock unlockWithCondition:lockTag];
- (NSMutableArray *)images
if(!_images)
_images = [NSMutableArray array];
return _images;
@end
递归锁
NSRecursiveLock
@interface NSRecursiveLockDemo ()
@property (nonatomic, strong) NSRecursiveLock *recursiveLock;
@end
@implementation NSRecursiveLockDemo
- (instancetype)init
self = [super init];
if (self)
self.recursiveLock = [[NSRecursiveLock alloc] init];
return self;
- (void)run
NSInteger n = 3;
NSLog(@"%ld! = %ld",(long)n, (long)[self sum:3]);
- (NSInteger)sum:(NSUInteger)n
[self.recursiveLock lock];
NSInteger result = 0;
if (n <= 1)
result = n;
else
result = [self sum:n - 1] * n;
[self.recursiveLock unlock];
return result;
@end
如果将 NSRecursiveLock
换成 NSLock
就会造成死锁。
@synchronized
- (void)lock1
@synchronized (self)
// 加锁操作
性能最差
以上是关于iOS - 多线程的锁的主要内容,如果未能解决你的问题,请参考以下文章