多线程-加锁

[大佬的理解各种锁]
(https://juejin.im/post/5a0a92996fb9a0451f307479)
【多线程-加锁】自己总结的,要自己写呀,要不记不住啊..
一份数据被多个线程引用就会出现安全隐患

多线程-加锁
文章图片
多线程操作.png 多线程-加锁
文章图片
线程加锁后.png 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_tattr; //创建 // 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 = https://www.it610.com/article/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操作,
临界区代码复杂或者循环量大
临界区竞争非常激烈

    推荐阅读