无标题文章

2016-12-22  本文已影响0人  张霸天

iOS中的多线程——锁

题记:虽然有些事情的发生可能是你预料之中的,但是当它真正的发生了的时候,还是很难以接受的,还是需要一点时间,去缓和这种消极的情绪,尽快站起来吧!加油!花了一天半的时间,各种查阅资料,总结了iOS中关于线程锁的知识,希望我能从中学到一些,也希望可以帮到同样有需要的你!(文中如有错误,还请提出,一起交流)

本文主要介绍:

1. 互斥锁(Mutex)

常用,当一个线程试图获取被另一个线程占用的锁时,它就会被挂起,让出CPU,直到该锁被释放。

互斥锁的实现方式:

1.@synchronized 同步锁

当然在Objective-C中你还可以用@synchronized指令快速的实现锁:

//主线程中
TestObj *obj = [[TestObj alloc] init];

//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    @synchronized(obj){
        [obj method1];
        sleep(10);
    }
});

//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);
    @synchronized(obj){
        [obj method2];
    }
});

@synchronized指令使用的obj为该锁的唯一标识,只有当标识相同时,才为满足互斥,如果线程2中的@synchronized(obj)改为@synchronized(other),刚线程2就不会被阻塞,@synchronized指令实现锁的优点就是我们不需要在代码中显式的创建锁对象,便可以实现锁的机制,但作为一种预防措施,@synchronized块会隐式的添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时候自动的释放互斥锁。所以如果不想让隐式的异常处理例程带来额外的开销,你可以考虑使用锁对象。

常用于单例模式的设计:

例程:

+(instancetype)shareInstance{
  // 1.定义一个静态实例,初值nil
  static TestSynchronized *myClass = nil;
  // 2.添加同步锁,创建实例
  @synchronized(self) {
      // 3.判断实例是否创建过,创建过则退出同步锁,直接返回该实例
      if (!myClass) {
          // 4.未创建过,则新建一个实例并返回
          myClass = [[self alloc] init];
      }
  }
  return myClass;
}

此时为了保证单例模式的更加严谨,需要重写allocWithZone方法,保证其他开发者使用allocinit方法时,不再创建新的对象。必要的时候还需要重写copyWithZone方法防止copy属性对单例模式的影响。

iOS中还有一种更加轻便的方法实现单例模式,即使用GCD中的dispatch_once函数实现。

例程:

+(instancetype)shareInstance{
    static TestSynchronized *myClass = nil;
    static dispatch_once_t once_token;
    dispatch_once(&once_token, ^{
        myClass = [[self alloc] init];
    });
    return myClass;
}

2.NSLock

例程:

static NSLock *mylock;
-(void)viewDidLoad {
  [super viewDidLoad];
  mylock = [[NSLock alloc] init];
}
-(void)myLockTest1{
  if ([mylock tryLock]) {
      // to do something
      [mylock unlock];
  }
}
-(void)myLockTest2{
  [mylock lock];
  // to do something
  [mylock unlock];
}

2. 递归锁(Recursive Lock)

递归锁可以被同一线程多次请求,而不会引起死锁,即在多次被同一个线程进行加锁时,不会造成死锁。这主要是用在循环或递归操作中。

可以允许同一线程多次加锁,而不会造成死锁。

递归锁会跟踪它被lock的次数。每次成功的lock都必须平衡调用unlock操作。只有所有达到这种平衡,锁最后才能被释放,以供其它线程使用。

递归锁会跟踪它被多少次lock。每次成功的lock都必须平衡调用unlock操作。只有所有的锁住和解锁操作都平衡的时候,锁才真正被释放给其他线程获得。

NSRecursiveLock *myRecursiveLock = [[NSRecursiveLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      static void (^MyRecursiveLockBlk)(int value);
      MyRecursiveLockBlk = ^(int value){
          [myRecursiveLock lock];
          if (value > 0) {
              // to do something
              NSLog(@"MyRecursiveLockBlk value = %d", value);
              MyRecursiveLockBlk(value - 1);
          }
          [myRecursiveLock unlock];
      };
      MyRecursiveLockBlk(6);
  });

此时如果将例程中的递归锁换成互斥锁:

NSRecursiveLock *myRecursiveLock = [[NSRecursiveLock alloc] init];换成
NSLock *myLock = [[NSLock alloc] init];,则会发生死锁问题。

3. 读写锁(Read-write Lock)

读写锁将访问者分为读出和写入两种,当读写锁在读加锁模式下,所有以读加锁方式访问该资源时,都会获得访问权限,而所有试图以写加锁方式对其加锁的线程都将阻塞,直到所有的读锁释放。

当在写加锁模式下,所有试图对其加锁的线程都将阻塞。

#import "ViewController.h"
#import <pthread.h>
@interface ViewController ()
@property(nonatomic, copy) NSString *rwStr;
@end
@implementation ViewController
pthread_rwlock_t rwlock;
-(void)viewDidLoad {
  [super viewDidLoad];
  // 初始化读写锁
  pthread_rwlock_init(&rwlock,NULL);
  __block int i;
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      i = 5;
      while (i>=0) {
          NSString *temp = [NSString stringWithFormat:@"writing == %d", i];
          [self writingLock:temp];
          i--;
      }  
  });
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      i = 5;
      while (i>=0) {
          [self readingLock];
          i--;
      }
  });
}
// 写加锁
-(void)writingLock:(NSString *)temp{
  pthread_rwlock_wrlock(&rwlock);
  // writing
  self.rwStr = temp;
  NSLog(@"%@", temp);
  pthread_rwlock_unlock(&rwlock);
}
// 读加锁
-(NSString *)readingLock{
  pthread_rwlock_rdlock(&rwlock);
  // reading
  NSString *str = self.rwStr;
  NSLog(@"reading == %@",self.rwStr);
  pthread_rwlock_unlock(&rwlock);
  return str;
}
@end

4. 自旋锁(Spin Lock)

自旋锁保持期间是抢占失效的

优点:效率高,不用进行线程的切换

缺点:如果一个线程霸占锁的时间过长,自旋会消耗CPU资源

// 头文件
#import <libkern/OSAtomic.h>
// 初始化自旋锁
static OSSpinLock myLock = OS_SPINLOCK_INIT;
// 自旋锁的使用
-(void)SpinLockTest{
  OSSpinLockLock(&myLock);
  // to do something
  OSSpinLockUnlock(&myLock);
}

5. 分布锁(Didtributed Lock)(进程间用的,不是线程)

6. 条件变量(Condition Variable)

NSCondition的例程:

// 创建锁
NSCondition *condition = [[NSCondition alloc] init];
static int count = 0;
// 生产者
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  while(count<20)
  {
      [condition lock];
      // 生产
      count ++;
      NSLog(@"生产 = %d",count);
      [condition signal];
      [condition unlock];
  }
});
// 消费者
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  while (count>]]>0)
  {
      [condition lock];
      // 消耗
      count --;
      NSLog(@"消耗剩余 = %d",count);
      [condition unlock];
  }
});

NSConditionLock的例程:

// 创建锁 
NSConditionLock *condLock = [[NSConditionLock alloc] initWithCondition:ConditionHASNOT];
static int count = 0; 
// 生产者
while(true) {  
  [condLock lock];  
  // 生产   
  count ++;   
  [condLock unlockWithCondition:ConditionHAS]; 
} 
  // 消费者 
while (true) {   
  [condLock lockWhenCondition:ConditionHAS];   
  // 消耗   
  count --;   
  [condLock unlockWithCondition:(count<=0 ? ConditionHASNOT : ConditionHAS)];
}

7. 信号量(Semaphore)

8. 栅栏/屏障(Barrier)

iOS中线程锁的性能对比:

上一篇下一篇

猜你喜欢

热点阅读