-
为什么用锁:
多线程编程中,如果对同一数据源进行读写操作就会造成不可预知的结果,所以我们应该尽量避免并发操作资源在线程之间共享,以减少线程间的相互作用,就需要一些同步工具,来确保当它们交互的时候是安全的。
-
锁的种类:
iOS开发中常用的锁有如下几种:
- @synchronized 同步锁
- NSLock 对象锁
- NSRecursiveLock 递归锁
- NSConditionLock 条件锁
- pthread_mutex 互斥锁(C语言)
- dispatch_semaphore 信号量实现加锁(GCD)
- OSSpinLock 自旋锁 (暂不建议使用,原因参见这里)
-
锁的性能对比:
我们先来看一下网上的评测,几乎关于锁性能介绍的文章都用了这张图:那根据这张图所描述的耗时对别正确吗?我们自己实际测试一下,看下边的测试代码:
#import
#import #define CycleTime (1024*1024*32) - (void)testAction { NSTimeInterval time = [NSDate date].timeIntervalSince1970; //同步锁 for (int i = 0; i < CycleTime; i++) { @synchronized(self) { } } NSLog(@"%f : work time of Synchronized",[NSDate date].timeIntervalSince1970-time); time = [NSDate date].timeIntervalSince1970; //条件锁 NSConditionLock *conditionLock = [[NSConditionLock alloc] init]; for (int i = 0; i < CycleTime; i++) { [conditionLock lock]; [conditionLock unlock]; } NSLog(@"%f : work time of NSConditionLock",[NSDate date].timeIntervalSince1970-time); time = [NSDate date].timeIntervalSince1970; //递归锁 NSRecursiveLock *rsLock = [[NSRecursiveLock alloc] init]; for (int i = 0; i < CycleTime; i++) { [rsLock lock]; [rsLock unlock]; } NSLog(@"%f : work time of NSRecursiveLock",[NSDate date].timeIntervalSince1970-time); time = [NSDate date].timeIntervalSince1970; //互斥锁 NSLock *mutexLock = [[NSLock alloc] init]; for (int i = 0; i < CycleTime; i++) { [mutexLock lock]; [mutexLock unlock]; } NSLog(@"%f : work time of NSLock",[NSDate date].timeIntervalSince1970-time); time = [NSDate date].timeIntervalSince1970; //互斥锁 pthread_mutex_t mutex; pthread_mutex_init(&mutex, NULL); for (int i = 0; i < CycleTime; i++) { pthread_mutex_lock(&mutex); pthread_mutex_unlock(&mutex); } NSLog(@"%f : work time of pthread_mutex",[NSDate date].timeIntervalSince1970-time); time = [NSDate date].timeIntervalSince1970; //信号量 dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); for (int i = 0; i < CycleTime; i++) { dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); dispatch_semaphore_signal(semaphore); } NSLog(@"%f : work time of dispatch_semaphore",[NSDate date].timeIntervalSince1970-time); time = [NSDate date].timeIntervalSince1970; //自旋锁 OSSpinLock spinlock = OS_SPINLOCK_INIT; for (int i = 0; i < CycleTime; i++) { OSSpinLockLock(&spinlock); OSSpinLockUnlock(&spinlock); } NSLog(@"%f : work time of OSSpinLock",[NSDate date].timeIntervalSince1970-time); } 输出:
2018-03-29 18:51:14.638390+0800 Lock[97416:4313533] 3.498765 : work time of Synchronized 2018-03-29 18:51:16.987938+0800 Lock[97416:4313533] 2.349282 : work time of NSConditionLock 2018-03-29 18:51:18.480868+0800 Lock[97416:4313533] 1.492687 : work time of NSRecursiveLock 2018-03-29 18:51:19.286578+0800 Lock[97416:4313533] 0.805475 : work time of NSLock 2018-03-29 18:51:20.029690+0800 Lock[97416:4313533] 0.742872 : work time of pthread_mutex 2018-03-29 18:51:20.556899+0800 Lock[97416:4313533] 0.526966 : work time of dispatch_semaphore 2018-03-29 18:51:20.909306+0800 Lock[97416:4313533] 0.352188 : work time of OSSpinLock
看输出结果一目了然了,跟上图的评测结果一致。
-
如何使用锁:
- @synchronized卖票例子:
- (void)sycnAction { __block int ticketsCount = 5; void(^saleTickets)(void) = ^() { while (YES) { /* 注意点: 1.加锁的代码尽量少 2.添加的OC对象必须在多个线程中都是同一对象 3.优点是不需要显式的创建锁对象,便可以实现锁的机制。 4.@synchronized块会隐式的添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时候自动的释放互斥锁。所以如果不想让隐式的异常处理例程带来额外的开销,你可以考虑使用锁对象。 */ //这里参数添加一个OC对象,一般使用self @synchronized(self) { [NSThread sleepForTimeInterval:1]; if (ticketsCount > 0) { ticketsCount--; NSLog(@"Tickets = %d, Thread:%@",ticketsCount,[NSThread currentThread]); } else { NSLog(@"Clear Thread:%@",[NSThread currentThread]); break; } } } }; NSLog(@"Start tickets count = %d, Thread:%@",ticketsCount,[NSThread currentThread]); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ saleTickets(); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ saleTickets(); }); }
- NSLock互斥锁卖票例子:
- (void)lockAction { __block int ticketsCount = 5; NSLock *lock = [[NSLock alloc] init]; void(^lockSaleTickets)(void) = ^() { while (1) { [lock lock]; [NSThread sleepForTimeInterval:1]; if (ticketsCount > 0) { ticketsCount--; NSLog(@"Tickets= %d, Thread:%@",ticketsCount,[NSThread currentThread]); } else { NSLog(@"Clear Thread:%@",[NSThread currentThread]); break; } [lock unlock]; } }; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ lockSaleTickets(); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ lockSaleTickets(); }); }
- NSRecursiveLock递归锁卖票例子:
- (void)recursiveLockAction { // NSLock *lock = [[NSLock alloc] init]; NSRecursiveLock *lock = [[NSRecursiveLock alloc] init]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ static void(^TestMethod)(int); TestMethod = ^(int value) { [lock lock]; if (value > 0) { NSLog(@"value = %d",value); [NSThread sleepForTimeInterval:1]; TestMethod(--value); } [lock unlock]; }; NSLog(@"Begain Test"); TestMethod(5); }); }
- NSConditionLock条件锁卖票例子:
- (void)conditionLockActive { NSConditionLock *lock = [[NSConditionLock alloc] init]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ for (int i = 0; i <= 5; i++) { [lock lock]; NSLog(@"1.thread1 condition = %ld, i = %d",(long)lock.condition,i); [lock unlockWithCondition:i]; NSLog(@"2.thread1 condition = %ld, i = %d",(long)lock.condition,i); } }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ [lock lockWhenCondition:2]; NSLog(@"thread2"); [lock unlock]; }); }
- pthread_mutex_t互斥锁例子:
- (void)pthreadMutexAction { __block pthread_mutex_t mutex; pthread_mutex_init(&mutex, NULL); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(2); pthread_mutex_lock(&mutex); NSLog(@"任务2"); pthread_mutex_unlock(&mutex); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ pthread_mutex_lock(&mutex); NSLog(@"任务1"); pthread_mutex_unlock(&mutex); }); }
- dispatch_semaphore_t信号量例子:
- (void)semaphoreAction { dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); __block int count = 10; void(^TestMethod)(void) = ^() { while (YES) { dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); if (count > 0) { count --; NSLog(@"value = %d, %@",count,[NSThread currentThread]); [NSThread sleepForTimeInterval:1]; } else { NSLog(@"Done %@",[NSThread currentThread]); break; } dispatch_semaphore_signal(semaphore); } }; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ TestMethod(); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ TestMethod(); }); }
- OSSpinLock自旋锁例子:
- (void)spinAction { __block int tickets = 5; OSSpinLock spinlock = OS_SPINLOCK_INIT; void(^TestMethod)(void) = ^() { while (YES) { OSSpinLockLock(&spinlock); if (tickets > 0) { tickets --; NSLog(@"value = %d, %@",tickets,[NSThread currentThread]); [NSThread sleepForTimeInterval:1]; } else { NSLog(@"Done %@",[NSThread currentThread]); break; } OSSpinLockUnlock(&spinlock); } }; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ TestMethod(); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ TestMethod(); }); }
-
死锁:待续....