起因是看到这篇 iOS中级面试题 ,说得比较中肯。很多知识点都接触过、也用过,但如果要答得好,还是得有系统的学习和总结。好记性不如烂笔头,所以就总结一下多线程中加锁的方法以及他们之间性能的比较。
原文地址: http://perpendiculo.us/2009/09/synchronized-nslock-pthread-osspinlock-showdown-done-right/
可能需要翻墙
稍微修改了原文的代码,因为文章是09年的,在Xcode7上面会报错。测试环境是非ARC
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
#import <libkern/OSAtomic.h>
#import <pthread.h>
#define ITERATIONS (1024*1024*32)
static unsigned long long disp=0, land=0;
typedef id(*_IMP) (id, SEL, ...);
int main(int argc, const char * argv[]) {
double then, now;
unsigned int i, count;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
OSSpinLock spinlock = OS_SPINLOCK_INIT;
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSLock *lock = [NSLock new];
then = CFAbsoluteTimeGetCurrent();
for(i=0;i<ITERATIONS;++i)
{
[lock lock];
[lock unlock];
}
now = CFAbsoluteTimeGetCurrent();
printf("NSLock: %f sec/n", now-then);
then = CFAbsoluteTimeGetCurrent();
_IMP lockLock = (_IMP)[lock methodForSelector:@selector(lock)];
_IMP unlockLock = (_IMP)[lock methodForSelector:@selector(unlock)];
for(i=0;i<ITERATIONS;++i)
{
lockLock(lock,@selector(lock));
unlockLock(lock,@selector(unlock));
}
now = CFAbsoluteTimeGetCurrent();
printf("NSLock+IMP Cache: %f sec/n", now-then);
then = CFAbsoluteTimeGetCurrent();
for(i=0;i<ITERATIONS;++i)
{
pthread_mutex_lock(&mutex);
pthread_mutex_unlock(&mutex);
}
now = CFAbsoluteTimeGetCurrent();
printf("pthread_mutex: %f sec/n", now-then);
then = CFAbsoluteTimeGetCurrent();
for(i=0;i<ITERATIONS;++i)
{
OSSpinLockLock(&spinlock);
OSSpinLockUnlock(&spinlock);
}
now = CFAbsoluteTimeGetCurrent();
printf("OSSpinlock: %f sec/n", now-then);
id obj = [NSObject new];
then = CFAbsoluteTimeGetCurrent();
for(i=0;i<ITERATIONS;++i)
{
@synchronized(obj)
{
}
}
now = CFAbsoluteTimeGetCurrent();
printf("@synchronized: %f sec/n", now-then);
[pool release];
return 0;
}
耗时结果比较:
OSSpinlock
< pthread_mutex
< NSLock+IMP
< NSLock
< @synchronized
NSLock: 1.437285 sec
NSLock+IMP Cache: 1.359244 sec
pthread_mutex: 0.972748 sec
OSSpinlock: 0.311700 sec
@synchronized: 2.888747 sec
分析:
1、@synchronized
内部会创建一个异常捕获的handler和其他内部使用的锁。所以会消耗大量的时间
2、NSLock 和 NSLock+IMP
两个时间非常接近。他们是 pthread mutexes
封装的,但是创建对象的时候需要额外的开销。
3、pthread_mutex
底层的API,性能比较高。
4、OSSpinLock
自旋锁不进入内核,仅仅是重新加载。如果自旋锁占用的时间是极少的(通常是纳秒级别的)性能还是比较高的,减少了系统的直接调用和上下文的切换。
但是,如果有资源竞争的时候,会占用更多的cpu,耗电严重。这个时候,使用 pthread_mutex
可以避免电量的消耗,虽然耗时多一些。