OC里的锁

编程入门 行业动态 更新时间:2024-10-09 10:24:21

<a href=https://www.elefans.com/category/jswz/34/1763299.html style=OC里的锁"/>

OC里的锁


互斥锁、自旋锁


互斥锁:保证在任何时候,都只有一个线程访问对象。当获取锁操作失败时,线程会进入睡眠,等待锁释放时被唤醒;
自旋锁:与互斥锁有点类似,只是自旋锁 不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环尝试,直到该自旋锁的保持者已经释放了锁;因为不会引起调用者睡眠,所以效率高于互斥锁;
自旋锁缺点:
调用者在未获得锁的情况下,一直运行--自旋,所以占用着CPU,如果不能在很短的时间内获得锁,会使CPU效率降低。所以自旋锁就主要用在临界区持锁时间非常短且CPU资源不紧张的情况下
在用自旋锁时有可能造成死锁,当递归调用时有可能造成死锁
两种锁的加锁原理

互斥锁:线程会从sleep(加锁)——>running(解锁),过程中有上下文的切换,cpu的抢占,信号的发送等开销。
自旋锁:线程一直是running(加锁——>解锁),死循环检测锁的标志位,机制不复杂。


递归锁

特殊的互斥锁,加了递归功能


ios中常见的几种锁


ios中常见的几种锁包括OSSpinLock、信号量(Semaphore)、pthread_mutex、NSLock、NSCondition、NSConditionLock、pthread_mutex(recursive)、NSRecursiveLock、synchronized

如下所示,测试锁性能的案例图(实际可能会略有偏差):

我们再选锁的时候,如果只是使用互斥锁的效果,那么按照性能排序选择靠前的即可,如果需要锁的一些其他功能,那么根据需要选择,不必过于局限于性能,毕竟实现功能与项目的维护也是非常重要的。
1.OSSpinLock/os_unfair_lock
由于OSSpinLock目前已经不再安全,存在bug,官方已放弃,iOS10之后os_unfair_lock取代OSSpinLock。

基本用法

#import <os/lock.h>@property(nonatomic,assign) os_unfair_lock unfairLock;//创建锁,    
self.unfairLock = OS_UNFAIR_LOCK_INIT;#pragma mark -- os_unfair_lock
-(void)unfairLock_test {os_unfair_lock_lock(&_unfairLock);self.count --;NSLog(@"%d",self.count);os_unfair_lock_unlock(&_unfairLock);
}
/*! @abstract Data type for a spinlock.@discussionYou should always initialize a spinlock to {@link OS_SPINLOCK_INIT} beforeusing it.*/
typedef int32_t OSSpinLock OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock);


2.dispatch_semaphore
dispatch_semaphore 是 GCD 用来同步的一种方式,与他相关的只有三个函数,一个是创建信号量,一个是等待信号,一个是发送信号。
```
dispatch_semaphore_create(long value);
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
dispatch_semaphore_signal(dispatch_semaphore_t dsema);
```

//如果信号量值<0,那么该函数就会一直等待(相当于阻塞当前线程)dispatch_semaphore_t sem = dispatch_semaphore_create(0);dispatch_async(dispatch_get_global_queue(0, 0), ^{NSLog(@"任务1");dispatch_semaphore_signal(sem);//+1不在阻塞线程继续执行});dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);//阻塞不在向下执行代码,阻塞到当前dispatch_async(dispatch_get_global_queue(0, 0), ^{NSLog(@"任务2");dispatch_semaphore_signal(sem);});dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);// 阻塞当前一下代码,等待上面dispatch_async(dispatch_get_global_queue(0, 0), ^{NSLog(@"任务3");});NSLog(@"任务4");


执行结果:

2022-07-25 15:51:07.697865+0800 instance[15452:17533217] 任务1
2022-07-25 15:51:07.698131+0800 instance[15452:17533358] 任务2
2022-07-25 15:51:07.698318+0800 instance[15452:17533217] 任务4
2022-07-25 15:51:07.698327+0800 instance[15452:17533358] 任务3


3.pthread_mutex
pthread互斥锁是 pthread 库中的一员,linux系统中中常用的库,使用时需要手动import导入 #import <pthread/pthread.h>,其中有 pthread_mutex_trylock为尝试加锁,如果没被加锁,则会加锁成功,并返回0,适用于一些优先级比较低,间歇性调用的功能
注意:其他部分锁也有trylock这个功能,例如 NSLock、NSRecursiveLock、NSConditionLock

 //非递归pthread_mutex_t lock0;pthread_mutex_init(&lock0, NULL);pthread_mutex_lock(&lock0);pthread_mutex_unlock(&lock0);pthread_mutex_destroy(&lock0);//递归pthread_mutex_t lock;pthread_mutexattr_t attr;pthread_mutexattr_init(&attr);pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);pthread_mutex_init(&lock, &attr);pthread_mutexattr_destroy(&attr);pthread_mutex_lock(&lock);pthread_mutex_unlock(&lock);pthread_mutex_destroy(&lock);


4.NSLock
NSLock 遵循 NSLocking协议,是常见的互斥锁之一,为 OC 框架中的 API,使用方便,据说是 pthread 封装的锁
 

[self.iLock lock];
self.count --;
NSLog(@"%d",self.count);
[self.iLock unlock];


5.NSCondition
NSCondition 算是一个稍微重量级的锁了,我理解为情景锁(另一个原因区分条件锁 NSConditionLock),适用于一些特殊场景,其也遵循 NSLocking协议,也属于互斥锁

#pragma mark -- NSCondition
- (void)nscondition_test {for (int i = 0; i < 50; i ++) {dispatch_async(dispatch_get_global_queue(0, 0), ^{[self lg_production];});}for (int i = 0; i < 100; i ++) {dispatch_async(dispatch_get_global_queue(0, 0), ^{[self lg_consumption];});}
}- (void)lg_production {[self.iCondition lock];self.count ++;NSLog(@"生产了一个产品,现有产品 : %d个",self.count);[self.iCondition signal];[self.iCondition unlock];
}
- (void)lg_consumption {[self.iCondition lock];while (self.count == 0) {[self.iCondition wait];}self.count --;NSLog(@"消费了一个产品,现有产品: %d个",self.count);[self.iCondition unlock];
}


6.NSConditionLock
NSConditionLock 被称为条件锁,其遵循 NSLocking 协议,即具备正常的互斥锁功能,此外加入了 条件语句,为其核心功能,即满足指定条件才会解锁,因此算是一个重量级的锁了,其同时可以理解为 NSCondition 进化版 ,如果你理解了 NSCondition的生产者-消费者模式,这个也会马上就明白了其原理了

- (void)lock 加锁。
- (void)unlock 解锁。
- (instancetype)initWithCondition:(NSinteger)初始化一个。NSConditionLock对象。
@property(readonly) NSInteger condition 锁的条件。
- (void)lockWhenCondition:(NSInteger)condition满足条件时加锁。
- (BOOL)tryLock尝试加锁。
- (BOOL)tryLockWhenCondition如果接受对象的condition与给定的condition相等,则尝试获取锁,不阻塞线程。
- (void)unlockWithCondition:(NSInteger)condition解锁,重置锁的条件。
- (BOOL)lockBeforDate:(NSDate *)limit在指定时间点之前获取锁。
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit在指定的时间前获取锁。
@property (nullable ,copy) NSString *name 锁名称。
 @property(nonatomic,strong) NSConditionLock *iConditionLock;self.iConditionLock = [[NSConditionLock alloc] initWithCondition:3];    dispatch_async(dispatch_get_global_queue(0, 0), ^{[self.iConditionLock lockWhenCondition:3];NSLog(@"线程 1");[self.iConditionLock unlockWithCondition:2];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{[self.iConditionLock lockWhenCondition:2];NSLog(@"线程 2");[self.iConditionLock unlockWithCondition:1];
});dispatch_async(dispatch_get_global_queue(0, 0), ^{[self.iConditionLock lockWhenCondition:1];NSLog(@"线程 3");[self.iConditionLock unlockWithCondition:0];
});


执行结果:

2022-07-25 16:23:34.215216+0800 instance[16302:17564433] 线程 1
2022-07-25 16:23:34.215412+0800 instance[16302:17564435] 线程 2
2022-07-25 16:23:34.215565+0800 instance[16302:17564437] 线程 3
#pragma mark --条件锁NSConditionLock,实现了NSLocking协议,支持默认的互斥锁lock、unlock
- (void)NSConditionLock {_conditionLock = [[NSConditionLock alloc] initWithCondition:1]; //可以更改值测试为0测试结果//加锁,当条件condition为传入的condition时,方能解锁//lockWhenCondition:(NSInteger)condition//更新condition的值,并解锁指定condition的锁//unlockWithCondition:(NSInteger)condition
}//多个队列执行条件锁
//通过案例可以看出,通过条件锁conditionLock可以设置线程依赖关系
//可以通过GCD设置一个具有依赖关系的任务队列么
- (void)NSConditionLockUpdate {//创建并发队列dispatch_queue_t queue = dispatch_queue_create("测试NSConditionLock", DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{if ([self->_conditionLock tryLockWhenCondition:1]) {NSLog(@"第一个");//默认初始conditon位1,所有能走到这里//然后解锁后,并设置初始值为4,解锁condition设定为4的线程[self->_conditionLock unlockWithCondition:4];}else {[self->_conditionLock lockWhenCondition:0];NSLog(@"第一个other");[self->_conditionLock unlockWithCondition:4];}});//由于开始初始化的conditon值为1,所以后面三个线程都不满足条件//锁定后直到condition调整为当前线程的condition时方解锁dispatch_async(queue, ^{//condition设置为2后解锁当前线程[self->_conditionLock lockWhenCondition:2];NSLog(@"第二个");//执行完毕后解锁,并设置condition为1,设置初始化默认值,以便于下次使用[self->_conditionLock unlockWithCondition:1];});dispatch_async(queue, ^{//condition设置为3后解锁当前线程[self->_conditionLock lockWhenCondition:3];NSLog(@"第三个");//执行完毕后解锁,并设置condition为2,解锁2[self->_conditionLock unlockWithCondition:2];});dispatch_async(queue, ^{//condition设置为4后解锁当前线程[self->_conditionLock lockWhenCondition:4];NSLog(@"第四个");//执行完毕后解锁,并设置condition为3,解锁3[self->_conditionLock unlockWithCondition:3];});
}


上面的流程可以大致简化为下面几步:
1.创建一个异步队列,以便于添加后续的任务依赖
2.逐步添加子任务模块,分别在不同线程中,其有明确的依赖关系,即执行顺序为 1、4、3、2
3.使用 lockWhenCondition:开始设置依赖,将其任务解锁的条件condition 设置为其特有的condition 号,以便于解锁
4.执行任务时,如果 NSCondition 中的 condition 参数,与本线程设置的tCondition不一样时,阻塞线程,等待 NSCondition 中的 condition 更改为指定值(通过 unlockWithCondition:更改condition值)解锁
即:默认初始化 condition 为 1,只有 任务1 能够执行,当 任务1 执行 unlockWithCondition:4时,condition被设置为4, 阻塞的任务4解锁,同理,任务4执行完毕后,将 condition 设置为 3 ,任务三解锁,依次类推
5.最终根据设置的依赖关系,分别执行 任务1、任务4、任务3、任务2

7.pthread_mutex(recursive)
其为基于 pthread框架 的递归锁,也是以 pthread互斥锁为基础实现的 递归锁,即:同一个线程下,递归调用时加锁,不会阻塞当前线程,当另一个线程到来时,会因为第一个线程加的锁而阻塞

#pragma mark --pthread递归锁
- (void)pthreadMutexRecursive {//初始化锁的递归功能pthread_mutexattr_t attr;pthread_mutexattr_init(&attr);pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);//互斥锁初始化时,绑定递归锁功能模块pthread_mutex_init(&_pMutexLock, &attr);//使用完毕后在合适的地方销毁,例如dealloc
//    pthread_mutexattr_destroy(&attr);
//    pthread_mutex_destroy(&_pMutexLock);
}//使用递归锁,递归地时候回不停加锁,如果使用普通的锁早已经形成死锁,无法解脱
//递归锁的存在就是在同一个线程中的锁,不会互斥,只会互斥其他线程的锁,从而避免死锁
- (void)pthreadMutexRecursiveUpdate {dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{static void (^recursiveBlock)(double count);recursiveBlock = ^(double count){pthread_mutex_lock(&self->_pMutexLock);if (count-- > 0) {self->_money++;recursiveBlock(count);}pthread_mutex_unlock(&self->_pMutexLock);};recursiveBlock(1000);});
}


8.NSRecursiveLock
和 pthread_mutex(recursive)一样,NSRecursiveLock 也是递归锁,其遵循 NSLocking 协议,即除了递归锁功能,还具备正常的互斥锁功能

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{static void (^recursiveBlock)(double count);recursiveBlock = ^(double count){[self.irecursiveLock lock];//tryLock就不多介绍了,和Pthread的类似,注意返回值即可//[self->_recursive tryLock];if (count-- > 0) {self.count++;NSLog(@"%d",self.count);recursiveBlock(count);}[self.irecursiveLock unlock];};recursiveBlock(1000);});


9.synchronized
synchronized 同步锁,即同步执行,以此避免多线程同时操作同一块代码,基本上在各个平台都会有其身影,虽然效率最低,但由于使用使用简单,深得大家喜爱

objc_sync_enter
要锁的代码
objc_sync_exit


例子:

#pragma mark --同步锁synchronized
- (void)synchronized {//使用简单,直接对代码块加同步锁,此代码不会被多个线程直接执行//可以间接理解为里面的任务被放到了一个同步队列依次执行(实际实现未知)@synchronized (self) {self->_money++;}
}


objc源码:

######### objc_sync_enter
// 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 nothingif (DebugNilSync) {_objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");}objc_sync_nil(); //如果对象不存在,什么事情都不做!}return result;
}######### SyncData
typedef struct alignas(CacheLineSize) SyncData {struct SyncData* nextData;DisguisedPtr<objc_object> object;int32_t threadCount;  // number of THREADS using this blockrecursive_mutex_t mutex; //递归锁
} SyncData;


@synchronized结论:
* 是对互斥锁的一种封装
* 具体点是种特殊的互斥锁->递归锁,内部搭配 nil防止死锁
* 通过表的结构存要锁的对象
* 表内部的对象又是通过哈希存储的
 

更多推荐

OC里的锁

本文发布于:2024-02-14 02:19:10,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1761194.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:OC

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!