[大佬的理解各种锁]
(https://juejin.im/post/5a0a92996fb9a0451f307479)
自己总结的,要自己写呀,要不记不住啊..
一份数据被多个线程引用就会出现安全隐患
1,iOS中的线程同步方案,分为自旋锁和互斥锁
1.1 OSSpinLock(自旋锁),
等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源
目前已经不再安全,可能会出现优先级反转问题
如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁
需要导入头文件#import
//初始化
OSSpinLock lock = OS_SPINLOCK_INIT;
//尝试加锁,如果需要等待就不加锁,直接返回false,如果不需要等待加锁就返回true
bool result = OSSpinLockTry(&lock);
//加锁
OSSpinLockLock(&lock);
//解锁
OSSpinLockUnLock(&lock);
@property (nonatomic ,assign) OSSpinLock lock;
-(void)viewDidLoad
{
[super viewDidLoad];
self.lock = OS_SPIN_LOCK_INIT;
}
-(void)lockTest
{
//尝试加锁,解决优先级较高的线程先调用的问题,避免造成死锁
if (OSSpinLockTry(&_lock)) {
int oldticketsCount = self.ticketsCount ;
sleep(.2);
oldticketsCount--;
self.ticketsCount = oldticketsCount;
//解锁
OSSpinlockUnlock(_lock);
}
//加锁
OSSpinLockLock(&_lock)
int oldticketsCount = self.ticketsCount ;
sleep(.2);
oldticketsCount--;
self.ticketsCount = oldticketsCount;
//解锁
OSSpinlockUnlock(_lock);
}
1.2 os_unfair_lock
os_unfair_lock用于取代不安全的OSSpinLock ,从iOS10开始才支持
从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等
需要导入头文件#import
//初始化
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
//尝试加锁
os_unfair_lock_trylock(&lock);
//加锁
os_fair_lock_lock(&lock);
//解锁
os_fair_lock_unlock(&lock);
1.3、pthread_mutex
1.3.1 常规锁
mutex叫做”互斥锁”,等待锁的线程会处于休眠状态
需要导入头文件#import
//初始化锁的属性
pthread_mutexattr_t attr; //创建属性
pthread_mutexattr_init(&attr);//舒适化属性
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_NOMAL);//设置属性类型
//初始化锁
pthread_metux_t mutex;
pthread_mutex_init(&mutex,&attr);
//尝试加锁
pthread_mutex_trylock(&mutex);
//加锁
pthread_mutex_lock(&mutex);
//解锁
pthread_mutex_unlock(&mutex);
//销毁资源
pthread_mutexattr_destroy(&attr);
pthread_mutex_destroy(&mutex);
//属性
@property (nonatomic , assign) pthread_mutex_t mutex;
-(void)viewDidLoad{
//初始化属性
// pthread_mutexattr_t attr;//创建
// pthread_mutexattr_init(&attr);//初始化
// pthread_mutexattr_settertype(&attr , PTHREAD_MUTEX_NORMAL);
//初始化互斥锁
// pthread_mutex_init(&_mutex , &attr);
直接等同于
pthread_mutex_init(&_mutex , NULL);
}
-(void)testMutex
{
//尝试加锁
pthread_mutex_trylock(&mutex);
//加锁
pthread_mutex_lock(&mutex);
//解锁
pthread_mutex_unlock(&mutex);
}
-(void)dealloc
{
//销毁相关资源
pthread_mutexattr_destory(&attr);
pthread_mutex_destory(&mutex);
}
1.3.2、递归锁
//初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
//初始化锁
pthread_mutex_t mutex;
pthread_mutex_init(&mutex,&attr);
//销毁资源
pthread_mutexattr_destroy(&attr);
pthread_mutex_destroy(&mutex);
1.3.2、条件锁
//初始化锁
pthread_mutex_t mutex;
//MULL表示使用默认属性
pthread_mutex_init(&mutex,NULL);
//初始化条件
pthread_cond_t condition;
pthread_cond_init(&condition,NULL);
//等待条件(进入休眠,放开mutex锁,被唤醒后,再次对mutex进行加锁)
pthread_cond_wait(&condition,&mutex);
//激活一个等待该条件的线程
pthread_cond_signal(&condition,&mutex);
//激活所有等待该条件的线程
pthread_cond_broadcast(&condition);
//销毁资源
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&condition);
1.4、NSLock、NSRecursiveLock
NSLock是对mutex普通锁的封装
@interface NSLock : NSObject {
-(BOOL)tryLock;
-(BOOL)lockBeforeDate:(NSDate *)limit;
}
@end
@protocol NSLocking
-(void)lock;
-(void)unlock
@end
//初始化锁
NSLock *lock = [[NSLock alloc]init];
NSRecursiveLock也是对mutex递归锁的封装,API和NSLock基本一致
1.5、NSCondition
NSCondition 是对mutex和cond的封装
@interface NSCondition : NSObject {
-(void)wait;
-(BOOL)waitUntilDate:(NSDate *)limit;
-(void)signal;
-(void)broadcast;
}
1.6、NSConditionLock
NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值,
@interface NSConditionLock : NSObject {
@property(readonly) NSInteger condition;
-(instancetype)initWithCondition:(NSInteger)condition;
-(BOOL)tryLock;
-(BOOL)tryLockWhenCondition:(NSInteger)condition;
-(void)unlockWithCondition:(NSInteger)condition;
-(BOOL)lockBeforeDate:(NSDate *)limit;
-(BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
@end
}
1.7、dispatch_semaphore
semaphore
叫做"信号量";
信号量的初始值,可以用来控制线程并发访问的最大数量,
信号量的初始值为1,代表同时只允许一条线程访问资源,保证线程同步
//信号量的初始值
int value = 1;
//初始化信号量
dispath_semaphore_t semaphore = dispatch_semaphore_create(value);
//如果信号量的值<=0,当前线程就会进入休眠等待(直到信号量的值>0)
//如果信号量>0,就减1,然后往下执行后面的代码
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
//让信号量的值加一
disparch_semaphore_signal(semaphore);
1.8、dispatch_queue
直接使用GCD的串行队列,也是可以实现线程同步的
dispatch_queue_t queue = dispatch_queue_create("lock_queue",DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
//任务
});
1.9、@synchronized
GNUstep是GNU计划的项目之一,它将Cocoa的OC库重新开源实现了一遍
源码地址:http://www.gnustep.org/resources/downloads.php
虽然GNUstep不是苹果官方源码,但还是具有一定的参考价值
@synchronized是对mutex递归锁的封装
源码查看:objc4中的objc-sync.mm文件
@synchronized(obj)
内部会生成obj
对应的递归锁,然后进行加锁、解锁操作
@synchronized(obj){
//做任务
}
2、iOS线程同步方案性能比较
性能从高到低排序
os_unfair_lock
OSSpinLock
dispatch_semaphore
pthread_mutex
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSCondition
pthread_mutex(recursive)
NSRecursiveLock
NSConditionLock
@synchronized
3、自旋锁和互斥锁比较
3.1、什么情况用自旋锁比较划算?
预计线程等待锁的时间很短
加锁的代码经常被调用,但竞争情况很少发生
cpu资源不紧张,多核处理器的
3.2、什么情况使用互斥锁比较划算?
预计线程等待锁的时间长,单核处理器,临界区有IO操作,
临界区代码复杂或者循环量大
临界区竞争非常激烈