多线程-加锁

[大佬的理解各种锁]
(https://juejin.im/post/5a0a92996fb9a0451f307479)

自己总结的,要自己写呀,要不记不住啊..

一份数据被多个线程引用就会出现安全隐患


多线程-加锁_第1张图片
多线程操作.png
多线程-加锁_第2张图片
线程加锁后.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_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操作,
临界区代码复杂或者循环量大
临界区竞争非常激烈

你可能感兴趣的:(多线程-加锁)