最近对于 NSCondition 感到有些疑惑,所以写点东西进行研究一下。
用法 首先 NSCondition
有以下几个方法:
1 2 3 4 5 - (void )wait; 让线程陷入睡眠,将锁的权利交出。 - (BOOL )waitUntilDate:(NSDate *)limit; 让线程睡眠到指定时间 - (void )signal; 发送消息唤醒一个睡眠中的线程 - (void )broadcast; 发送广播唤醒所有睡眠中的线程
wait/waitUntilDate: 在文档里,对于wait
方法的描述是:
1 2 Blocks the current thread until the condition is signaled. You must lock the receiver prior to calling this method.
封锁当前线程直到接收到唤醒操作,调用这个方法前,必须先执行lock
方法。
对于waitUntilDate:
的描述如下:
1 2 3 4 5 6 7 8 9 10 11 12 Declaration - (BOOL )waitUntilDate:(NSDate *)limit; Parameters limit The time at which to wake up the thread if the condition has not been signaled. Return Value YES if the condition was signaled; otherwise, NO if the time limit was reached.Discussion You must lock the receiver prior to calling this method.
封锁当前线程,假如在指定时间内未收到唤醒,那么将自动唤醒且返回NO
,收到唤醒则返回YES
,同样调用这个方法前,必须先执行lock
方法。
signal/broadcast signal
描述如下:
1 2 3 4 5 6 7 8 9 Signals the condition, waking up one thread waiting on it. Declaration - (void )signal; Discussion You use this method to wake up one thread that is waiting on the condition. You may call this method multiple times to wake up multiple threads. If no threads are waiting on the condition, this method does nothing. To avoid race conditions, you should invoke this method only while the receiver is locked.
发送唤醒信号,唤醒一个等待中的线程,可以多次发送唤醒多个睡眠中的线程,假如没有等待的线程,这个方法 does nothing ~ 为了线程安全,必须先执行 lock
方法。
broadcast
描述如下:
1 2 3 4 5 6 7 Declaration - (void )broadcast; Discussion If no threads are waiting on the condition, this method does nothing. To avoid race conditions, you should invoke this method only while the receiver is locked.
即唤醒所有等待中的线程。
疑惑点 看到这里我有几个疑惑:
wait
方法是否是将锁的权利交了出去?
signal
后被唤醒的线程是否立即可以执行操作了呢?还是等待锁被释放才能进行进一步操作呢?
带着这两个疑惑我写了下面的代码进行测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 NSCondition *lock = [NSCondition new]; dispatch_async (dispatch_get_global_queue(0 , 0 ), ^{ [lock lock]; NSLog (@"A线程加锁" ); [lock wait]; NSLog (@"A线程被唤醒" ); [lock unlock]; }); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC )), dispatch_get_main_queue(), ^{ dispatch_async (dispatch_get_global_queue(0 , 0 ), ^{ [lock lock]; NSLog (@"B线程加锁" ); [lock signal]; NSLog (@"B线程唤醒" ); [lock unlock]; }); });
打印结果如下:
1 2 3 4 2018-11-16 00:01:39.090049+0800 VoucherCollection[69550:12039642] A线程加锁 2018-11-16 00:01:40.090205+0800 VoucherCollection[69550:12039641] B线程加锁 2018-11-16 00:01:40.090587+0800 VoucherCollection[69550:12039641] B线程被唤醒 2018-11-16 00:01:40.090907+0800 VoucherCollection[69550:12039642] A线程唤醒
可以看出,A 线程睡眠后,B 线程会获取到锁,说明wait
操作会把锁的权利交出去,解答了第一个疑惑。
然后我们将 B 线程中的最后一句注释掉,变成:
1 2 3 4 5 6 7 8 9 10 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC )), dispatch_get_main_queue(), ^{ dispatch_async (dispatch_get_global_queue(0 , 0 ), ^{ [lock lock]; NSLog (@"B线程加锁" ); [lock signal]; NSLog (@"B线程唤醒" ); }); });
打印结果如下:
1 2 3 2018-11-16 00:06:57.739508+0800 VoucherCollection[69640:12056072] A线程加锁 2018-11-16 00:06:58.739682+0800 VoucherCollection[69640:12056073] B线程加锁 2018-11-16 00:06:58.740058+0800 VoucherCollection[69640:12056073] B线程唤醒
可以看到 B 在执行唤醒操作后没有进行解锁,这时候 A 没有执行唤醒后的后续操作。这说明,A 被唤醒后,因为没有锁的权利,只能等待锁被 B 释放掉才能执行后续操作,这时候 A 才又获取到了锁的权利。
这里还有个前提需要注意,假如 A 在被唤醒的时候,还有其他的线程在占用锁,同样 A 要进行等待,可以验证一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 NSCondition *lock = [NSCondition new];dispatch_async (dispatch_get_global_queue(0 , 0 ), ^{ [lock lock]; NSLog (@"A线程加锁" ); [lock wait]; NSLog (@"A线程唤醒" ); [lock unlock]; }); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC )), dispatch_get_main_queue(), ^{ dispatch_async (dispatch_get_global_queue(0 , 0 ), ^{ [lock lock]; NSLog (@"B线程加锁" ); [lock signal]; NSLog (@"B线程唤醒" ); [lock unlock]; }); dispatch_async (dispatch_get_global_queue(0 , 0 ), ^{ [lock lock]; NSLog (@"C线程加锁" ); sleep(3 ); [lock unlock]; }); });
打印结果如下:
1 2 3 4 5 2018-11-16 00:19:41.909029+0800 VoucherCollection[69838:12094270] A线程加锁 2018-11-16 00:19:43.006895+0800 VoucherCollection[69838:12094271] B线程加锁 2018-11-16 00:19:43.007118+0800 VoucherCollection[69838:12094271] B线程唤醒 2018-11-16 00:19:43.007295+0800 VoucherCollection[69838:12094273] C线程加锁 2018-11-16 00:19:46.011014+0800 VoucherCollection[69838:12094270] A线程唤醒
可以看出,只有 C 解锁以后 A 才会执行。
生产者消费者模式 生产者生产,消费者消费,缓存区是固定大小的。 以下是用NSCondition
实现的该模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 NSCondition *lock = [NSCondition new];dispatch_async (dispatch_get_global_queue(0 , 0 ), ^{ while (YES ) { [lock lock]; if (self .arr.count == 0 ) { [lock wait]; } [self .arr removeLastObject]; NSLog (@"%@" ,self .arr); [lock signal]; [lock unlock]; sleep(2 ); } }); dispatch_async (dispatch_get_global_queue(0 , 0 ), ^{ while (YES ) { [lock lock]; if (self .arr.count >= 5 ) { [lock wait]; } [self .arr addObject:@"new resource" ]; NSLog (@"%@" ,self .arr); [lock signal]; [lock unlock]; sleep(1 ); } });
对于单生产者单消费者,这些应该是没问题的,多对一或者多对多的情况,改天再分析~