信号量:dispatch_semaphore_t
互斥锁:pthread_mutex、@ synchronized、NSLock
条件锁:NSConditionLock 、NSCondition、
递归锁:NSRecursiveLock
自旋锁:OSSpinLock(不安全,已遗弃,会出现优先级反转导致死锁的问题)
读写锁:atomic(iOS10之后是os_unfair_lock
来实现的)
自旋锁和互斥锁的区别
自旋锁会忙等: 所谓忙等,即在访问被锁资源时,调用者线程不会休眠,而是不停循环在那里,直到被锁资源释放锁。
互斥锁会休眠: 所谓休眠,即在访问被锁资源时,调用者线程会休眠,此时cpu可以调度其他线程工作。直到被锁资源释放锁。此时会唤醒休眠线程。
按性能由高到低:
一、信号量dispatch_semaphore_t :性能高
// 创建信号量,并给信号量初始化值 dispatch_semaphore_t dispatch_semaphore_create(long value); // 信号量减1,当信号量<=0时阻塞线程 long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout); // 信号量加1 long dispatch_semaphore_signal(dispatch_semaphore_t dsema);
控制资源的同步访问
// 创建信号量并初始化信号量的值为0 dispatch_semaphore_t semaphone = dispatch_semaphore_create(0); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 线程1 sleep(2); NSLog(@"async1.... %@",[NSThread currentThread]); dispatch_semaphore_signal(semaphone);//信号量+1 }); dispatch_semaphore_wait(semaphone, DISPATCH_TIME_FOREVER);//信号量减1 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 线程2 sleep(2); NSLog(@"async2.... %@",[NSThread currentThread]); dispatch_semaphore_signal(semaphone);//信号量+1 });
最大并发数
// 创建信号量并初始值为5,最大并发量5 dispatch_semaphore_t semaphore = dispatch_semaphore_create(5); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); for (int i = 0;i < 100 ; i ++) { dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); dispatch_async(queue, ^{ NSLog(@"i = %d %@",i,[NSThread currentThread]); //此处模拟一个 异步下载图片的操作 sleep(arc4random()%6); dispatch_semaphore_signal(semaphore); }); }
二、互斥锁pthread_mutex_t
@property(nonatomic, assign)pthread_mutex_t _mutex; //初始化 pthread_mutex_init(&_mutex, NULL); //加锁 pthread_mutex_lock(&_mutex); //操作 //解锁 pthread_mutex_unlock(&_mutex); //销毁 pthread_mutex_destroy(&_mutex);
三、互斥锁NSLock(对pthread_mutex_t的封装)
//初始化 __block NSLock *lock = [[NSLock alloc] init]; //加锁 [lock lock]; //解锁 [lock unlock];
四、条件锁NSCondition(对pthread_mutex_t的封装)
- (void)conditionLock { NSCondition *conditionLock = [[NSCondition alloc] init]; __block NSString *food; // 消费者1 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [conditionLock lock]; if (!food) {// 没有现成的菜(判断是否满足线程阻塞条件) NSLog(@"等待上菜"); [conditionLock wait];// 没有菜,等着吧!(满足条件,阻塞线程) } // 菜做好了,可以用餐!(线程畅通,继续执行) NSLog(@"开始用餐:%@",food); [conditionLock unlock]; }); // 消费者2 // dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // [conditionLock lock]; // if (!food) { // NSLog(@"等待上菜2"); // [conditionLock wait]; // } // NSLog(@"开始用餐2:%@",food); // [conditionLock unlock]; // }); // 生产者 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [conditionLock lock]; NSLog(@"厨师做菜中..."); sleep(5); food = @"四菜一汤"; NSLog(@"厨师做好了菜:%@",food); [conditionLock signal]; // [conditionLock broadcast]; [conditionLock unlock]; }); }
五、条件锁NSConditionLock(对NSCondition的封装)
// 类似于信号量 NSConditionLock * conditionLock = [[NSConditionLock alloc] initWithCondition:2]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ [conditionLock lockWhenCondition:1]; NSLog(@"线程1"); [conditionLock unlockWithCondition:0]; }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ [conditionLock lockWhenCondition:2]; NSLog(@"线程2"); [conditionLock unlockWithCondition:1]; }); dispatch_async(dispatch_get_global_queue(0, 0), ^{ [conditionLock lock]; NSLog(@"线程3"); [conditionLock unlock]; });
打印结果:
线程3 线程2 线程1 或者 线程2 线程1 线程3
分析:
-lockWhenCondition: 加锁,阻塞线程,如果condition与属性相等,被唤醒
-unlockWithCondition: 解锁,并修改condition属性
[conditionLock lock] 没有条件 多线程和 [conditionLock lockWhenCondition:2] 谁先执行,不确定
六、递归锁NSRecursiveLock(对pthread_mutex_t的封装)
七、互斥锁@synchronized : 性能低,更简洁易读易用
@synchronized (self) {
}
另外
栅栏函数控制多线程同步dispatch_barrier_sync 和 dispatch_barrier_async
// 并发队列 dispatch_queue_t queue = dispatch_queue_create("com.gcd.brrier", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ sleep(1); NSLog(@"任务1 -- %@",[NSThread currentThread]); }); dispatch_async(queue, ^{ sleep(2); NSLog(@"任务2 -- %@",[NSThread currentThread]); }); // 栅栏函数,修改同步栅栏和异步栅栏,观察“栅栏结束”的打印位置 dispatch_barrier_sync(queue, ^{ for (int i = 0; i < 4; i++) { NSLog(@"任务3 --- log:%d -- %@",i,[NSThread currentThread]); } }); // 在这里执行一个输出 NSLog(@"栅栏结束"); dispatch_async(queue, ^{ sleep(1); NSLog(@"任务4 -- %@",[NSThread currentThread]); }); dispatch_async(queue, ^{ sleep(2); NSLog(@"任务5 -- %@",[NSThread currentThread]); });
原子操作atomic,原理是属性的set get方法加了锁,atomic是读/写操作安全的(操作的原子性),但不能保证多线程安全;nonatomic不能保证读写操作的安全(操作非原子性),也不能保证多线程安全;但是nonatomic的性能比atomic高,如果不涉及多线程操作,使用nonatomic是不错的选择,因为他可以保证性能的同时还能确保数据安全;如果开发中涉及到大量多线程操作,那么务必使用atomic,因为相对于性能而言,数据的正确安全更为重要。能在性能和安全之间找到平衡是最考验程序员的!!!
// 添加一个atomic属性 @property (atomic, copy) NSString *name;
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT); dispatch_async(concurrentQueue, ^{// 线程1 self.name = @"张三";//setter操作 }); dispatch_async(concurrentQueue, ^{// 线程2 self.name = @"李四";//setter操作 }); dispatch_async(concurrentQueue, ^{// 线程3 self.name = @"王二";//setter操作 // 这里有一个稍微耗时的操作,完事后想利用name的值,因为模拟需求是要把名字叫王二的人叫来 sleep(2); NSLog(@"叫王二来一趟,name = %@",self.name);//getter操作 }); dispatch_async(concurrentQueue, ^{// 线程4 self.name = @"麻子";//setter操作 });
打印结果:叫王二来一趟,name = 麻子
扩展:
一、三方库 SDWebImage 中的锁:
@property (nonatomic, strong) dispatch_semaphore_t lock;
懒加载
- (dispatch_semaphore_t)lock { if (!_lock) { _lock = dispatch_semaphore_create(1); } return _lock; }
宏定义
#ifndef SD_LOCK #define SD_LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); #endif #ifndef SD_UNLOCK #define SD_UNLOCK(lock) dispatch_semaphore_signal(lock); #endif
加锁
SD_LOCK(self.lock);
解锁
SD_UNLOCK(self.lock);