故事
老师为了得到一列学生的名字,可以对自己的学生这么说:
我会发给第一个同学一张纸,第一个同学把纸向后传,每个通过学都重复上一个同学的动作,直到传到最后一名学生。最后一名学生写名字再往回传,每个同学再重复上一个同学的动作,直到第一个同学。
这就是一个相对完整的递归。
对应关系
纸 ------------------ 公共资源
第一个同学 --------- 入口
向后传 ------------- 回调
最后一个同学 ------- 出口
写名字 ------------- 对于公共资源的操作
往回传 ------------- 返回
递归的特征
-
递归的入口也是出口:
纸是从第一个人接手的,也是最后从第一个人送回的。 -
自己回调自己:
除了
最后一名学生,每个人都是在重复上一个人的动作。 -
出口条件:
直到传到
最后一名学生才会结束向后传。 -
公共资源:
一般情况下递归会轮番对一个
公共资源进行传递,直到传递到出口条件,然后再反向传递回来。
为什么递归不能用互斥锁(NSLock)?
互斥锁在本故事中可以理解为:我没有在这个纸上写下名字之前谁都不准写。互斥锁从第一个人就开始给这张纸加锁,所以到了最后一名同学要开始写名字的时候已经加了一堆锁了。但是最后一名同学不在纸上写完名字又不能传回去。这就产生了悖论,也就是死锁。
递归锁(NSRecursiveLock)的工作描述
递归锁本身相当于一个令牌,有了这个令牌我们就能在纸上写东西,然后这个令牌每传一次可使用次数加以,传到最后一个同学后开始使用令牌,每使用一次,可使用次数减一。当然只有拿到过这个令牌的人才有资格用他。
多列(Thread)同时使用一个递归锁(NSRecursiveLock)的情况
如果多列(线程)同时使用一个递归锁(令牌)则照样会导致死锁,不用多解释,你就想想一下好几列(线程)的人一起抢一个令牌啥场面?为啥会造成死锁也能想象出来了吧。
更加安全,更加厚重的递归锁(synchronized)
这时候如果还能再令牌上不仅标识使用次数,还把之前已经传递了哪些同学,这些同学属于那列(Thread)。也就不会出现问题了。但是这样令牌也会变得厚重,而且效率降低。但是换来的却是更加的安全。
代码部分
如果是简单的防死锁用NSLock
void code_testLock(void) {
__block int a = 0;
NSLock *lock = [[NSLock alloc] init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[lock lock];
a++;
[lock unlock];
});
}
如果是递归方法则需要用NSRecursiveLock
void code_testLock(void) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
addMethod = ^(int a){
[lock lock];
if (a < 10) {
NSLog(@"%d", a);
addMethod(a+1);
}
[lock unlock];
};
addMethod(0);
});
}
但是如果这么执行就会死锁
代码1
for (int i= 0; i<100; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^testMethod)(int);
testMethod = ^(int value){
[lock lock];
if (value > 0) {
NSLog(@"current value = %d",value);
testMethod(value - 1);
}
[lock unlock];
};
testMethod(10);
});
}
但是区别于下面的方式,下面的方法并不会造成死锁
for (int i = 0; i < 1000; i++) {
addMethod = ^(int a){
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[lock lock];
if (a < 10) {
NSLog(@"%d", a);
addMethod(a+1);
}
[lock unlock];
});
};
addMethod(0);
}
这时候就要用synchronized
for (int i= 0; i<100; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^testMethod)(int);
testMethod = ^(int value){
@synchronized(lock) {
if (value > 0) {
NSLog(@"current value = %d",value);
testMethod(value - 1);
}
}
};
testMethod(10);
});
}
源码部分: juejin.cn/post/684490…