ios线程安全

来源:互联网 发布:C 释放动态数组内存 编辑:程序博客网 时间:2024/06/16 07:23

在iOS开发中,支持多种同步方法,我们从耗时角度出发,评估各种同步对象的性能。

  1. @synchronized
  2. NSLock
  3. NSCondition
  4. NSConditionLock
  5. NSRecursiveLock
  6. pthread_mutex_t
  7. OSSpinLock
  8. dispatch_barrier_async

示例代码如下:

const NSInteger KRunTimes = 1000 *1000;


  double curTime, lastTime;


  // 1.同步synchronized

  id obj = [NSObject new];


  lastTime = CFAbsoluteTimeGetCurrent();

  for (NSInteger i =0; i < KRunTimes; ++i) {

    @synchronized(obj) {

    }

  }

  curTime = CFAbsoluteTimeGetCurrent();

  NSLog(@"@synchronized: %f ms", (curTime - lastTime) *1000);


  // 2.NSLock

  NSLock *myLock = [NSLock new];


  lastTime = CFAbsoluteTimeGetCurrent();

  for (NSInteger i =0; i < KRunTimes; ++i) {

    [myLock lock];

    [myLock unlock];

  }

  curTime = CFAbsoluteTimeGetCurrent();

  NSLog(@"NSLock: %f ms", (curTime - lastTime) *1000);


  // NSLock IMP

  typedefvoid (*func)(id, SEL);

  SEL lockSEL = @selector(lock);

  SEL unlockSEL = @selector(unlock);

  func lockFunc = (void (*)(id, SEL))[myLock methodForSelector : lockSEL];

  func unlockFunc = (void (*)(id, SEL))[myLock methodForSelector : unlockSEL];


  lastTime = CFAbsoluteTimeGetCurrent();

  for (NSInteger i =0; i < KRunTimes; ++i) {

    lockFunc(myLock, lockSEL);

    unlockFunc(myLock, unlockSEL);

  }


  curTime = CFAbsoluteTimeGetCurrent();

  NSLog(@"NSLock + IMP: %f ms", (curTime - lastTime) *1000);


  // 3.NSCondition

  NSCondition *condition = [[NSCondition alloc] init];

  lastTime = CFAbsoluteTimeGetCurrent();

  for (NSInteger i =0; i < KRunTimes; ++i) {

    [condition lock];

    [condition unlock];

  }

  curTime = CFAbsoluteTimeGetCurrent();

  NSLog(@"NSCondition: %f ms", (curTime - lastTime) *1000);


  // 4.NSConditionLock

  NSConditionLock *conditionLock = [[NSConditionLock alloc] init];

  lastTime = CFAbsoluteTimeGetCurrent();

  for (NSInteger i =0; i < KRunTimes; ++i) {

    [conditionLock lock];

    [conditionLock unlock];

  }

  curTime = CFAbsoluteTimeGetCurrent();

  NSLog(@"NSConditionLock: %f ms", (curTime - lastTime) *1000);


  // 5.NSRecursiveLock

  NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];

  lastTime = CFAbsoluteTimeGetCurrent();

  for (NSInteger i =0; i < KRunTimes; ++i) {

    [recursiveLock lock];

    [recursiveLock unlock];

  }

  curTime = CFAbsoluteTimeGetCurrent();

  NSLog(@"NSRecursiveLock: %f ms", (curTime - lastTime) *1000);


  // 6.pthread_mutex_t

  pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;


  lastTime = CFAbsoluteTimeGetCurrent();

  for (NSInteger i =0; i < KRunTimes; ++i) {

    pthread_mutex_lock(&mutex);

    pthread_mutex_unlock(&mutex);

  }


  curTime = CFAbsoluteTimeGetCurrent();

  NSLog(@"pthread_mutex: %f ms", (curTime - lastTime) *1000);

  pthread_mutex_destroy(&mutex);


  // 7.OSSpinLock 自旋锁;

  OSSpinLock spinlock = OS_SPINLOCK_INIT;

  lastTime = CFAbsoluteTimeGetCurrent();

  for (NSInteger i =0; i < KRunTimes; ++i) {

    OSSpinLockLock(&spinlock);

    OSSpinLockUnlock(&spinlock);

  }


  curTime = CFAbsoluteTimeGetCurrent();

  NSLog(@"OSSpinlock: %f ms", (curTime - lastTime) *1000);


  // 8 dispatch_barrier_async

  dispatch_queue_t queue =

      dispatch_queue_create("com.qq.ksnow", DISPATCH_QUEUE_CONCURRENT);


  lastTime   = CFAbsoluteTimeGetCurrent();

  for (NSInteger i =0; i < KRunTimes; ++i) {

    dispatch_barrier_async(queue, ^{});

  }

  curTime = CFAbsoluteTimeGetCurrent();

  NSLog(@"@dispatch_barrier_async: %f ms", (curTime - lastTime) *1000);

一.模拟器/iOS7/XCode6下性能对比

日志情况:

2014-09-0711:26:48.071LockTest[2713:98107] @synchronized:232.551038ms


2014-09-0711:26:48.173LockTest[2713:98107]NSLock:100.879967ms

2014-09-0711:26:48.263LockTest[2713:98107]NSLock + IMP: 89.570999ms

2014-09-0711:26:48.353LockTest[2713:98107]NSCondition:89.850008ms

2014-09-0711:26:48.587LockTest[2713:98107]NSConditionLock:233.431995ms

2014-09-0711:26:48.677LockTest[2713:98107]NSRecursiveLock:89.230001ms

2014-09-0711:26:48.740LockTest[2713:98107]pthread_mutex:62.326968ms

2014-09-0711:26:48.750LockTest[2713:98107]OSSpinlock:10.430992ms

2014-09-0711:26:49.985LockTest[2713:98107]dispatch_barrier_async:1234.429002ms

总结对比


二.iPad Mini2/iOS7/XCode6下性能对比

2014-09-0713:32:47.720LockTest[3494:60b] @synchronized:499.736011ms

2014-09-0713:32:47.948LockTest[3494:60b]NSLock:227.194011ms

2014-09-0713:32:48.170LockTest[3494:60b]NSLock + IMP: 221.384048ms

2014-09-0713:32:48.393LockTest[3494:60b]NSCondition:221.689999ms

2014-09-0713:32:49.030LockTest[3494:60b]NSConditionLock:636.340976ms

2014-09-0713:32:49.260LockTest[3494:60b]NSRecursiveLock:229.423046ms

2014-09-0713:32:49.431LockTest[3494:60b]pthread_mutex:170.615971ms

2014-09-0713:32:49.495LockTest[3494:60b]OSSpinlock:63.916981ms

2014-09-0713:32:49.826LockTest[3494:60b]dispatch_barrier_async:329.769015ms


总结

  • 耗时方面:
    • OSSpinlock耗时最少;
    • pthread_mutex其次。
    • NSLock/NSCondition/NSRecursiveLock 耗时接近,220ms上下居中。
    • NSConditionLock最差,我们常用synchronized倒数第二。
    • dispatch_barrier_async也许,性能并不像我们想象中的那么好.推测与线程同步调度开销有关。单独block耗时在1ms以下基本上可以忽略不计的。
  • 在访问次数比较多的情况下,IMP访问耗时要比消息发送略小。 参看NSLockNSLock + IMP 对比。
  • 为什么不直接使用 IMP 而使用直接声明函数指针的方式? XCode6下IMP不能指定参数,报错如下


  • 如何解决呢?修改LLVM预处理设置即可。详情见XCode6下Too many arguments to function call, expected 0, have 2解决办法

原因分析

1.synchronized

会创建一个异常捕获handler和一些内部的锁。所以,使用@synchronized替换普通锁的代价是,你付出更多的时间消耗。


创建给@synchronized指令的对象是一个用来区别保护块的唯一标示符。如果你在两个不同的线程里面执行上述方法,每次在一个线程传递了一个不同的对象给anObj参数,那么每次都将会拥有它的锁,并持续处理,中间不被其他线程阻塞。然而,如果你传递的是同一个对象,那么多个线程中的一个线程会首先获得该锁,而其他线程将会被阻塞直到第一个线程完成它的临界区。

作为一种预防措施,@synchronized块隐式的添加一个异常处理例程来保护代码。该处理例程会在异常抛出的时候自动的释放互斥锁。这意味着为了使用@synchronized指令,你必须在你的代码中启用异常处理。了如果你不想让隐式的异常处理例程带来额外的开销,你应该考虑使用锁的类。

2.NSConditionLock

条件锁,与特定的,用户定义的条件有关。可以确保一个线程可以获取满足一定条件的锁。

内部会涉及到信号量机制,一旦一个线程获得锁后,它可以放弃锁并设置相关条件;其它线程竞争该锁。

线程之间的竞争激烈,涉及到条件锁检测,线程间通信。系统调用,上下切换方切换比较频繁。

3.OSSpinLock

自旋锁几乎不进入内核,仅仅是重新加载自旋锁。

如果自旋锁被占用时间是几十,上百纳秒,性能还是挺高的。减少了代价较高的系统调用和一系列上下文言切换。

但是,该锁不是万能的;如果该锁抢占比较多的时候,不要使用该锁。会占用较多cpu,导致耗电较多。

这种情况下使用pthread_mutex虽然耗时多一点,但是,避免了电量过多的消耗。是不错的选择。


官方描述:

Spin locks are a simple, fast, thread-safe synchronization primitive thatis suitablein situations where contentionis expectedto be low. The spinlock operations use memory barriersto synchronize accesstoshared memoryprotectedby the lock. Preemptionis possiblewhile the lock is held.

4.pthread_mutex

底层的API还是性能比较高啊,在各种同步对象中,性能属于佼佼者。

结论:

  • 如果只是粗略的使用锁,不考虑性能问题可以使用synchronized。
  • 如果对效率有较高的要求,还是采用OSSpinlock比较好。
  • 因为Pthread的锁在也是用 OSSpinlock 实现的。OSSpinlock 的实现过程中,并没有进入系统kernel,使用OSSpinlock可以节省系统调用和上下文切换。

原址 http://www.tuicool.com/articles/rQNZruA
0 0