1.NSCondition
1.需求:生产者与消费者,生产者生产商品,消费者消耗商品,只有生产者产出的商品个数大于0时,消费者才能消费,否则等待生产者生产商品!
2.实现:
- (void)viewDidLoad {
[super viewDidLoad];
self.productCount = 0;
[self wm_conditon];
}
- (void)wm_conditon {
_condition = [[NSCondition alloc] init];
//创建生产-消费者
for (int i = 0; i < 50; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self wm_producers];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self wm_consumers];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self wm_producers];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self wm_consumers];
});
}
}
- (void)wm_producers{
[_condition lock];
self.productCount = self.productCount + 1;
NSLog(@"生产者生产一个 现有%zd个",self.productCount);
//发出消息
[_condition signal];
[_condition unlock];
}
- (void)wm_consumers{
// 线程安全
[_condition lock];
while (self.productCount == 0) {
NSLog(@"等待 count = %zd",self.productCount);
// 保证正常流程
[_condition wait];
}
//注意消费行为,要在等待条件判断之后
self.productCount -= 1;
NSLog(@"消费者消费一个 还剩%zd个",self.productCount);
[_condition unlock];
}
-
NSCondition类实现了一个条件变量,该变量遵循posix条件的语义。条件对象在给定线程中充当锁和检查点。锁在测试条件并执行由条件触发的任务时保护锁内的代码。检查点行为要求在线程继续执行其任务之前,条件必须为真。当条件不为真时,线程阻塞,且一直处于阻塞状态,直到另一个线程向条件对象发出信号。
//⼀般⽤于多线程同时访问、修改同⼀个数据源 //保证在同⼀时间内数据源只被访问、修改⼀次 //其他线程的命令需要在lock外一直等待,直到unlock,才可访问 [condition lock]; //与lock同时使⽤ [condition unlock]; //让当前线程处于等待状态 [condition wait]; //CPU发信号告诉线程不⽤再等待,可以继续执⾏ [condition signal];
2.NSConditionLock
NSConditionLock是锁,⼀旦⼀个线程获得锁,其他线程⼀定等待
//表示获得锁,如果没有其他线程获得锁(不需要判断内部的condition条件) ,那它能执⾏此⾏以下代码
//如果已经有其他线程获得锁(可能是条件锁,或者⽆条件锁,有条件锁需要判断condition内部条件),则需要等待,直⾄其他线程解锁
[conditionLock lock];
//如果没有其他线程获得该锁,但是该锁内部的condition条件不等于A条件,此行以下代码不能获得锁,进入等待状态
//如果锁内部的condition条件等于A条件,并且没有其他线程获得该锁,则进⼊代码区
//同时设置它获得该锁,其他任何线程都将等待它的代码完成,直⾄它解锁。
[conditionLock lockWhenCondition:A条件];
//表示释放锁,同时把内部的condition条件设置为A条件
[conditionLock unlockWithCondition:A条件];
//表示如果被锁定(没获得锁),并超过A时间则不再阻塞线程。
//但是注意:此时返回的值是NO!
return [conditionLock lockWhenCondition:A条件 beforeDate:A时间];
//所谓的condition条件就是NSInteger类型的整数,内部通过整数⽐较条件
- 看一段代码:
- (void)wm_conditonLock {
// 类似于信号量
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];
});
}
//打印结果是什么?
//321或213
/**
分析:
任务1调⽤[NSConditionLock lockWhenCondition:],此时此刻因为不满⾜当前条件,
所以会进⼊ waiting 状态,当前进⼊到 waiting 时,会释放当前的互斥锁。
任务2调用[NSConditionLock lockWhenCondition:],因为满⾜条件值,所以任务2会打印,
打印完成后会调⽤[NSConditionLock unlockWithCondition:],这个时候将 value 设置为1,并发送广播boradcast,
此时任务1接收到当前的信号,唤醒执⾏并打印。所以1在2的后面打印!
任务3调⽤[NSConditionLock lock:],本质上是调⽤[NSConditionLock lockBeforeDate:],
这⾥不需要⽐对条件值,所以任务3会打印
[NSConditionLock lockWhenCondition:]:这⾥会根据condition内部条件值和传入的Value值进⾏对⽐,
如果不相等,这⾥就会阻塞,进⼊线程池,否则的话就继续代码执⾏
[NSConditionLock unlockWithCondition:]:这⾥会先将condition内部的条件值更改为当前的value值,
然后进⾏⼴播,唤醒其他获取条件锁内部条件值的为value的线程。
*/
3.NSConditionLock探究
-
运行我们上面NSConditionLock的代码
-
我们添加断点,运行,调试Xcode,选择Debug->Debug Workflow ->Always Show Disassembly,去看汇编的调用流程!
-
添加-[NSConditionLock lockWhenCondition:]符号断点,同样的,我们可以添加-[NSConditionLock unlockWithCondition:]符号断点!现在过掉上一个断点:
-
打印了一堆和NSDate相关的,那我们猜想这个应该是和时间相关的!去看了一下文档,搜索一下lockWhenCondition,文档给出了相关的函数有两个: 1.lockWhenCondition:beforeDate: 2.unlockWithCondition:,第二个我们知道,那第一个也正好和Date相关,那是不是苹果在我们调用lockWhenCondition:方法时,按照苹果的习惯,工厂模式去调用了lockWhenCondition:beforeDate:方法!现在我们去打印一下第二objc_msgSend:
-
上面已经看到,却是去调用了lockWhenCondition:beforeDate:这个方法,那就相同思路,再去添加一个符号断点-[NSConditionLock lockWhenCondition:beforeDate:],过掉lockWhenCondition:断点:
-
上面的打印能够看出,NSConditionLock是对NSCondition的封装!那这里是封装了NSCondition,加了一把锁,但是条件在哪处理的呢?
-
不相等,则调用了waitUntilDate:方法,表示线程进入等待状态!那么我们现在把断点过掉,进到任务二的流程去看看compare x8和x21的比较,因为前面的流程都一样:
-
至此,整个流程算是完成一个闭环!
-
其实在swift源码中,我们也能看到这样一个逻辑!这里使用这种方法来探究整个流程,就是为了学习这样一种探析方法,当我们没有源码的时候也能去试着探析底层原理及流程!