iOS 中的八大锁
1、NSLock
NSLock 遵循 NSLocking 协议,lock 方法是加锁,unlock 是解锁,tryLock 是尝试加锁,如果失败的话返回 NO,lockBeforeDate: 是在指定Date之前尝试加锁,如果在指定时间之前都不能加锁,则返回NO
@protocol NSLocking - (void)lock; - (void)unlock; @end @interface NSLock : NSObject <NSLocking> { @private void *_priv; } - (BOOL)tryLock; - (BOOL)lockBeforeDate:(NSDate *)limit; @property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0); @end
2、NSConditionLock
@interface NSConditionLock : NSObject <NSLocking> { @private void *_priv; } - (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER; @property (readonly) NSInteger condition; - (void)lockWhenCondition:(NSInteger)condition; - (BOOL)tryLock; - (BOOL)tryLockWhenCondition:(NSInteger)condition; - (void)unlockWithCondition:(NSInteger)condition; - (BOOL)lockBeforeDate:(NSDate *)limit; - (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit; @property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0); @end
//主线程中 NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:0]; //线程1 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [lock lockWhenCondition:1]; NSLog(@"线程1"); sleep(2); [lock unlock]; }); //线程2 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1);//以保证让线程2的代码后执行 if ([lock tryLockWhenCondition:0]) { NSLog(@"线程2"); [lock unlockWithCondition:2]; NSLog(@"线程2解锁成功"); } else { NSLog(@"线程2尝试加锁失败"); } }); //线程3 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(2);//以保证让线程2的代码后执行 if ([lock tryLockWhenCondition:2]) { NSLog(@"线程3"); [lock unlock]; NSLog(@"线程3解锁成功"); } else { NSLog(@"线程3尝试加锁失败"); } }); //线程4 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(3);//以保证让线程2的代码后执行 if ([lock tryLockWhenCondition:2]) { NSLog(@"线程4"); [lock unlockWithCondition:1]; NSLog(@"线程4解锁成功"); } else { NSLog(@"线程4尝试加锁失败"); } }); 2016-08-19 13:51:15.353 ThreadLockControlDemo[1614:110697] 线程2 2016-08-19 13:51:15.354 ThreadLockControlDemo[1614:110697] 线程2解锁成功 2016-08-19 13:51:16.353 ThreadLockControlDemo[1614:110689] 线程3 2016-08-19 13:51:16.353 ThreadLockControlDemo[1614:110689] 线程3解锁成功 2016-08-19 13:51:17.354 ThreadLockControlDemo[1614:110884] 线程4 2016-08-19 13:51:17.355 ThreadLockControlDemo[1614:110884] 线程4解锁成功 2016-08-19 13:51:17.355 ThreadLockControlDemo[1614:110884] 线程1
上面代码先输出了 ”线程 2“,因为线程 1 的加锁条件不满足,初始化时候的 condition 参数为 0,而加锁条件是 condition 为 1,所以加锁失败。locakWhenCondition 与 lock 方法类似,加锁失败会阻塞线程,所以线程 1 会被阻塞着,而 tryLockWhenCondition 方法就算条件不满足,也会返回 NO,不会阻塞当前线程。
回到上面的代码,线程 2 执行了 [lock unlockWithCondition:2]; 所以 Condition 被修改成了 2。
而线程 3 的加锁条件是 Condition 为 2, 所以线程 3 才能加锁成功,线程 3 执行了 [lock unlock]; 解锁成功且不改变 Condition 值。
线程 4 的条件也是 2,所以也加锁成功,解锁时将 Condition 改成 1。这个时候线程 1 终于可以加锁成功,解除了阻塞。
从上面可以得出,NSConditionLock 还可以实现任务之间的依赖。
3、NSRecursiveLock
4、NSCondition
5、@synchronized
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ @synchronized(self) { sleep(2); NSLog(@"线程1"); } NSLog(@"线程1解锁成功"); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1); @synchronized(self) { NSLog(@"线程2"); } }); 2016-08-19 16:42:21.752 ThreadLockControlDemo[2220:208291] 线程1 2016-08-19 16:42:21.752 ThreadLockControlDemo[2220:208291] 线程1解锁成功 2016-08-19 16:42:21.752 ThreadLockControlDemo[2220:208278] 线程2
@synchronized(object) 指令使用的 object 为该锁的唯一标识,只有当标识相同时,才满足互斥,所以如果线程 2 中的 @synchronized(self) 改为@synchronized(self.view),则线程2就不会被阻塞,@synchronized 指令实现锁的优点就是我们不需要在代码中显式的创建锁对象,便可以实现锁的机制,但作为一种预防措施,@synchronized 块会隐式的添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时候自动的释放互斥锁。@synchronized 还有一个好处就是不用担心忘记解锁了。
如果在 @sychronized(object){} 内部 object 被释放或被设为 nil,从我做的测试的结果来看,的确没有问题,但如果 object 一开始就是 nil,则失去了锁的功能。不过虽然 nil 不行,但 @synchronized([NSNull null]) 是完全可以的
6、dispatch_semaphore
dispatch_semaphore 是 GCD 用来同步的一种方式,与他相关的只有三个函数,一个是创建信号量,一个是等待信号,一个是发送信号
dispatch_semaphore_create(long value); dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout); dispatch_semaphore_signal(dispatch_semaphore_t dsema);
dispatch_semaphore_t signal = dispatch_semaphore_create(1);//创建一个信号量(semaphore),允许同时并发的操作最多只有1次 dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_semaphore_wait(signal, overTime);//等待,直到信号量大于0时,即可操作,同时将信号量-1 或者超时 sleep(2); NSLog(@"线程1"); dispatch_semaphore_signal(signal);//信号通知,即让信号量+1 }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1); dispatch_semaphore_wait(signal, overTime); NSLog(@"线程2"); dispatch_semaphore_signal(signal); });
dispatch_semaphore 和 NSCondition 类似,都是一种基于信号的同步方式,但 NSCondition 信号只能发送,不能保存(如果没有线程在等待,则发送的信号会失效)。而 dispatch_semaphore 能保存发送的信号。dispatch_semaphore 的核心是 dispatch_semaphore_t 类型的信号量。
dispatch_semaphore_create(1) 方法可以创建一个 dispatch_semaphore_t 类型的信号量,设定信号量的初始值为 1。注意,这里的传入的参数必须大于或等于 0,否则 dispatch_semaphore_create 会返回 NULL。
dispatch_semaphore_wait(signal, overTime) 方法会判断 signal 的信号值是否大于 0。大于 0 不会阻塞线程,消耗掉一个信号,执行后续任务。如果信号值为 0,该线程会和 NSCondition 一样直接进入 waiting 状态,等待其他线程发送信号唤醒线程去执行后续任务,或者当 overTime 时限到了,也会执行后续任务。
dispatch_semaphore_signal(signal) 发送信号,如果没有等待的线程接受信号,则使 signal 信号值加一(做到对信号的保存)。
从上面的实例代码可以看到,一个 dispatch_semaphore_wait(signal, overTime); 方法会去对应一个 dispatch_semaphore_signal(signal); 看起来像 NSLock 的 lock 和 unlock,其实可以这样理解,区别只在于有信号量这个参数,lock unlock 只能同一时间,一个线程访问被保护的临界区,而如果 dispatch_semaphore 的信号量初始值为 x ,则可以有 x 个线程同时访问被保护的临界区。
7、OSSpinLock
__block OSSpinLock theLock = OS_SPINLOCK_INIT; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ OSSpinLockLock(&theLock); NSLog(@"线程1"); sleep(10); OSSpinLockUnlock(&theLock); NSLog(@"线程1解锁成功"); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1); OSSpinLockLock(&theLock); NSLog(@"线程2"); OSSpinLockUnlock(&theLock); }); 2016-08-19 20:25:13.526 ThreadLockControlDemo[2856:316247] 线程1 2016-08-19 20:25:23.528 ThreadLockControlDemo[2856:316247] 线程1解锁成功 2016-08-19 20:25:23.529 ThreadLockControlDemo[2856:316260] 线程2
8、pthread_mutex
互斥锁的使用过程中,主要有pthread_mutex_init,pthread_mutex_destory,pthread_mutex_lock,pthread_mutex_unlock这几个函数以完成锁的初始化,锁的销毁,上锁和释放锁操作。
pthread_mutex_t _lock; //初始化 pthread_mutex_init(&_lock, NULL);
//上锁 pthread_mutex_lock(&_lock) //这里加入可能产生死锁的操作 比如像文件写入 读取之类 //解锁 pthread_mutex_unlock(&_lock)
pthread_mutex_destroy(&_lock)
浙公网安备 33010602011771号