线程锁的几种介绍
- 互斥锁(又名同步锁)。即只有当锁处于打开状态下才能被使用。NSLock,或者@synchronized,C里用
pthread_mutex_t
。说的都是一个东西,这个没什么好说的,就是简单,好用,线程安全的好帮手,主要是防止几个线程同时操作一个可变容器。- 递归锁。NSRecursiveLock主要是用在循环递归的时候来加锁的。这个东西是允许重复加锁,只要加锁和解锁的次数一致就会释放这个锁(同一线程内部受这个限制,不同线程才需要他收支平衡才能开锁),否则即死锁。
- 条件锁。NSConditionLock当满足条件的时候这个锁才会解开。当我们在使用多线程的时候,有时一把只会lock和unlock的锁未必就能完全满足我们的使用。因为普通的锁只能关心锁与不锁,而不在乎用什么钥匙才能开锁,而我们在处理资源共享的时候,多数情况是只有满足一定条件的情况下才能打开这把锁。
下面举例说明下
NSLock *theLock = [[NSLock alloc] init];
// 创建递归方法
static void (^testCode)(int);
testCode = ^(int value) {
[theLock lock];
if (value > 0)
{
NSLog(@"int:%d",value);
[NSThread sleepForTimeInterval:1];
testCode(value - 1);
}
NSLog(@"next");
[theLock unlock];
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
testCode(5);
});
这个栗子有兴趣的朋友可以run下,根本走不到打印next的那,为什么呢??
原因很简单,因为当进入到递归里的时候,刚进入
testCode(value - 1);
的时候就走不动了,因为这个锁被互斥了,被锁住了。所以下面的代码都不能往下走了。这就是互斥的好处,安全。但不好的是,这里并没有达到我们的预期,没有轮询往下走。
现在我们改下。把NSLock改成我们的递归锁
NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init];
然后我们继续走,这次就不会被锁死了,会一直到出现1为止。
这就是递归锁的神奇之处,因为我们是在一个线程里,所以不会因为我一直没解锁,还终止递归。
NSConditionLock 相关知识点:
- NSConditionLock 是锁,一旦一个线程获得锁,其他线程一定等待
- [xxxx lock]; 表示 xxx 期待获得锁,如果没有其他线程获得锁(不需要判断内部的condition) 那它能执行此行以下代码,如果已经有其他线程获得锁(可能是条件锁,或者无条件锁),则等待,直至其他线程解锁
- [xxx lockWhenCondition:A条件]; 表示如果没有其他线程获得该锁,但是该锁内部的condition不等于A条件,它依然不能获得锁,仍然等待。如果内部的condition等于A条件,并且没有其他线程获得该锁,则进入代码区,同时设置它获得该锁,其他任何线程都将等待它代码的完成,直至它解锁。
- [xxx unlockWithCondition:A条件]; 表示释放锁,同时把内部的condition设置为A条件
下面看个栗子
NSConditionLock *lock = [[NSConditionLock alloc] init];
NSMutableArray *products=[NSMutableArray array];
NSInteger HAS_DATA=1;
NSInteger NO_DATA=0;
// 线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
while(1){
[lock lockWhenCondition:NO_DATA];
[products addObject:[[NSObject alloc] init]];
NSLog(@"produceaproduct,总量:%zi",products.count);
[lock unlockWithCondition:HAS_DATA];
sleep(1);
}
});
// 线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
while(1){
[lock lockWhenCondition:HAS_DATA];
NSLog(@"waitforproduct");
[products removeObjectAtIndex:0];
NSLog(@"customeaproduct");
[lock unlockWithCondition:NO_DATA];
}
});
解释下:
因为我们默认的没有给condition,所以是0,故能进入线程1,进入线程1后,到最后会用1的条件来让别人解锁。同时让自身的condition重置成1,这样做为了让线程2不在等待。这样就能顺利的让线程2接力,线程2在结束的时候又把球抛给线程1,这样1来2去,就不会死锁。
以上便是常用的锁的常用解释。这些都是线程锁。
下面还有一个进程锁,可以用来堵塞进程的,信号量。
信号量的创建:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
这个参数必须是>=0不然这个semaphore就会返回null。
dispatch_semaphore_wait信号量-1,dispatch_semaphore_signal信号量+1.
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
// 线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"yyy");
[NSThread sleepForTimeInterval:5];
dispatch_semaphore_signal(semaphore);
});
// 线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"xxxx");
dispatch_semaphore_signal(semaphore);
});
如果是semaphore创建的时候信号量是0的时候,谁也别想走了,全部挂起。
线程1里,进去就休眠5秒,在这期间,谁也不能run,全部挂起,直到5秒后,信号量+1后才都恢复生机。线程2也一样的逻辑。