多线程--锁

本文主要介绍:

互斥锁

递归锁

读写锁

自旋锁

分布锁

条件变量

信号量

栅栏

一些常用锁的性能。

1. 互斥锁(Mutex)

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

互斥锁的实现方式:

@synchronized:实现单例模式

NSLock:不能迭代加锁,如果发生两次lock,而未unlock过,则会产生死锁问题。

1.@synchronized同步锁

例程:

/**

*设置属性值

*/

-(void)setMyTestString:(NSString *)myTestString{

  @synchronized(self) {

      // todo something

      _myTestString = myTestString;

  }

}

常用于单例模式的设计:

例程:

+(instancetype)shareInstance{

  // 1.定义一个静态实例,初值nil

  static TestSynchronized *myClass = nil;

  // 2.添加同步锁,创建实例

  @synchronized(self) {

      // 3.判断实例是否创建过,创建过则退出同步锁,直接返回该实例

      if (!myClass) {

          // 4.未创建过,则新建一个实例并返回

          myClass = [[self alloc] init];

      }

  }

  return myClass;

}

此时为了保证单例模式的更加严谨,需要重写allocWithZone方法,保证其他开发者使用alloc和init方法时,不再创建新的对象。必要的时候还需要重写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操作。只有所有达到这种平衡,锁最后才能被释放,以供其它线程使用。

例程:

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

@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

// 初始化自旋锁

static OSSpinLock myLock = OS_SPINLOCK_INIT;

// 自旋锁的使用

-(void)SpinLockTest{

  OSSpinLockLock(&myLock);

  // to do something

  OSSpinLockUnlock(&myLock);

}

5. 分布锁(Didtributed Lock)

跨进程的分布式锁,是进程间同步的工具,底层是用文件系统实现的互斥锁,并不强制进程休眠,而是起到告知的作用。

NSDistributedLock没有实现NSLocking协议,所以没有会阻塞线程的lock方法,取而代之的是非阻塞的tryLock方法来获取锁,用unlock方法释放锁。

如果一个获取锁的进程在释放锁之前就退出了,那么锁就一直不能释放,此时可以通过breakLock强行获取锁。

6. 条件变量(Condition Variable)

使用情况:如果一个线程需要等待某一条件出现才能继续执行,而这个条件是由别的线程产生的,这个时候就用到条件变量。常见的情况是:生产者-消费者问题。

条件变量可以让一个线程等待某一条件,当条件满足时,会收到通知。在获取条件变量并等待条件发生的过程中,也会产生多线程的竞争,所以条件变量通常和互斥锁一起工作。

NSCondition:是互斥锁和条件锁的结合,即一个线程在等待signal而阻塞时,可以被另一个线程唤醒,由于操作系统实现的差异,即使没有发送signal消息,线程也有可能被唤醒,所以需要增加谓词变量来保证程序的正确性。

NSConditionLock:与NSCondition的实现机制不一样,当定义的条件成立的时候会获取锁,反之,释放锁。

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)

信号量:可以是一种特殊的互斥锁,可以是资源的计数器

可以使用GCD中的Dispatch Semaphore实现,Dispatch Semaphore是持有计数的信号,该计数是多线程编程中的计数类型信号。计数为0时等待,计数大于等于1时,减1为不等待。

8. 栅栏/屏障(Barrier)

栅栏必须单独执行,不能与其他任务并发执行,栅栏只对并发队列有意义。

栅栏只有等待当前队列所有并发任务都执行完毕后,才会单独执行,带起执行完毕,再按照正常的方式继续向下执行。

iOS中线程锁的性能对比:

点击这里,参考网址

No1.自旋锁OSSpinLock耗时最少

No2.pthread_mutex

No3.NSLock/NSCondition/NSRecursiveLock耗时接近

No4.@synchronized

No5.NSConditionLock

栅栏的性能并没有很好,在实际开发中也很少用到(笔者在最近一次面试中就遇到,问栅栏的性能怎么样?当时并不知道栅栏在实际应用中的性能并不是很理想,又被问到苹果官方常使用的锁是什么?应该是自旋锁,然而笔者当时还是不知道。。。)

原文链接:http://www.jianshu.com/p/a236130bf7a2

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