- (void)method1 {
NSLog(@"%@", @"线程1");
}
- (void)method2 {
NSLog(@"%@", @"线程2");
}
- (void)method3 {
NSLog(@"%@", @"线程3");
}
NSLock是Cocoa提供给我们最基本的锁对象,这也是我们经常所使用的,除lock和unlock方法外,NSLock还提供了tryLock和lockBeforeDate:两个方法,前一个方法会尝试加锁,如果锁不可用(已经被锁住),刚并不会阻塞线程,并返回NO。lockBeforeDate:方法会在所指定Date之前尝试加锁,如果在指定时间之前都不能加锁,则返回NO。
///NSLock锁
NSLock *lock = [[NSLock alloc] init];
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
[self method1];
sleep(10);
[lock unlock];
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);//以保证让线程2的代码后执行
[lock lock];
[self method2];
[lock unlock];
});
执行打印结果:
2018-05-26 09:07:00.966149+0800 AAAA[23178:827090] 线程1
2018-05-26 09:07:10.968050+0800 AAAA[23178:827091] 线程2
通过打印结果可以看到,加锁的线程1在执行完整个任务后,线程2才开始执行。
如果加锁后,锁没有及时释放掉,则会造成死锁现象,当线程1执行之后,其他线程将无法执行。
NSLock锁较为常用,通常是添加在一个线程中,要注意的是添加锁的线程不要是多次执行的,因为添加锁之后,其他线程要等待锁执行之后才能执行,所以添加锁的的代码最好是不耗时的。
@synchronized指令实现锁的优点就是我们不需要在代码中显式的创建锁对象,便可以实现锁的机制,但作为一种预防措施,@synchronized块会隐式的添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时候自动的释放互斥锁。所以如果不想让隐式的异常处理例程带来额外的开销,你可以考虑使用锁对象。
UIView *other = [[UIView alloc] init];
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(self){
[self method1];
sleep(10);
}
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
@synchronized(other){
[self method2];
}
});
//线程3
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
@synchronized(self){
[self method3];
}
});
执行打印结果:
2018-05-26 09:15:03.896815+0800 AAAA[23309:843305] 线程1
2018-05-26 09:15:04.901968+0800 AAAA[23309:843304] 线程2
2018-05-26 09:15:13.902446+0800 AAAA[23309:843307] 线程3
通过结果可以看出,@synchronized锁对应的为同一个对象时,锁才会生效,当对象为self时,锁的效果执行,线程阻塞,当对象为other时和self的锁不冲突,可以同步执行。相比于NSLock,它的创建更为简介,并且可以执行多条线程的加锁。
__block pthread_mutex_t mutex;
__block pthread_mutex_t mutex2;
pthread_mutex_init(&mutex, NULL);
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&mutex);
[self method1];
sleep(5);
pthread_mutex_unlock(&mutex);
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
pthread_mutex_lock(&mutex2);
[self method2];
pthread_mutex_unlock(&mutex2);
});
//线程3
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
pthread_mutex_lock(&mutex);
[self method3];
pthread_mutex_unlock(&mutex);
});
执行打印结果:
2018-05-26 09:31:33.218339+0800 AAAA[23561:869507] 线程1
2018-05-26 09:31:34.219542+0800 AAAA[23561:869506] 线程2
2018-05-26 09:31:38.223177+0800 AAAA[23561:869508] 线程3
pthread_mutex_t定义在pthread.h,所以记得#include
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);
[self method1];
sleep(10);
dispatch_semaphore_signal(semaphore);
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[self method2];
dispatch_semaphore_signal(semaphore);
});
GCD锁执行打印的效果与C语言的执行是一样的,书写方法的不同而已。
NSLock *theLock = [[NSLock alloc] init];
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void(^TestMethod)(int);
TestMethod = ^(int value) {
[theLock lock];
if (value > 0)
{
[self method1];
sleep(4);
TestMethod(value-1);
}
[theLock unlock];
};
TestMethod(5);
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
[theLock lock];
[self method2];
[theLock unlock];
});
执行打印结果:
2018-05-26 09:40:55.891359+0800 AAAA[23673:888329] 线程1
此现象,为死锁现象,为什么会产生这种情况,因为在线程1中,每次循环之后,都重新block回调执行一次线程1的任务,theLock进行了多次的锁lock,自己把自己给锁住了,产生了死锁现象,这里就要引出NSRecursiveLock递归锁了。上面的代码中如果将theLock的类型NSLock替换为NSRecursiveLock递归锁,就不会产生死锁了。
执行打印结果:
2018-05-26 09:44:19.823501+0800 AAAA[23727:894828] 线程1
2018-05-26 09:44:23.829966+0800 AAAA[23727:894828] 线程1
2018-05-26 09:44:27.837278+0800 AAAA[23727:894828] 线程1
2018-05-26 09:44:31.843270+0800 AAAA[23727:894828] 线程1
2018-05-26 09:44:35.849868+0800 AAAA[23727:894828] 线程1
2018-05-26 09:44:39.853977+0800 AAAA[23727:894829] 线程2
NSRecursiveLock类定义的锁可以在同一线程多次lock,而不会造成死锁。递归锁会跟踪它被多少次lock。每次成功的lock都必须平衡调用unlock操作。只有所有的锁住和解锁操作都平衡的时候,锁才真正被释放给其他线程获得。
在线程中,有时需要对应的条件去启动某一个线程的任务,这时NSLock不一定能满足我们的条件,这个时候需要NSConditionLock条件锁来满足我们。
正如NSConditionLock的命名一样,在加锁的时候我们可以额外添加对应的条件来控制我们的线程开启。
NSConditionLock *theLock = [[NSConditionLock alloc] init];
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i=0;i<=3;i++)
{
[theLock lock];
NSLog(@"thread1:%d",i);
sleep(2);
[theLock unlockWithCondition:i];
}
});
//线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[theLock lockWhenCondition:2];
NSLog(@"thread2");
[theLock unlock];
});
//线程3
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[theLock lockWhenCondition:3];
NSLog(@"thread3");
[theLock unlock];
});
执行打印结果:
2018-05-26 09:53:30.565448+0800 AAAA[23875:912929] thread1:0
2018-05-26 09:53:32.570947+0800 AAAA[23875:912929] thread1:1
2018-05-26 09:53:34.575138+0800 AAAA[23875:912929] thread1:2
2018-05-26 09:53:36.580697+0800 AAAA[23875:912929] thread1:3
2018-05-26 09:53:38.582872+0800 AAAA[23875:912930] thread3
这里有两个地方要注意,1.首先是在线程1中,我们添加了条件锁的入口
[theLock unlockWithCondition:i];
而在线程2线程3中我们同时设置了两个接收条件的锁。i=2和i=3,为什么thread2没有执行,是因为条件锁,在整个执行完之后才会去触发他的条件,执行完整个任务后i=3所以只触发了线程3的任务。那我们如果把线程2的触发条件改成i=3,我们执行的结果为
2018-05-26 09:57:58.681922+0800 AAAA[23989:921838] thread1:0
2018-05-26 09:58:00.687457+0800 AAAA[23989:921838] thread1:1
2018-05-26 09:58:02.689869+0800 AAAA[23989:921838] thread1:2
2018-05-26 09:58:04.694757+0800 AAAA[23989:921838] thread1:3
2018-05-26 09:58:06.700193+0800 AAAA[23989:921836] thread2
2018-05-26 09:58:06.700547+0800 AAAA[23989:921839] thread3
这也就证明了,条件锁对应的锁是1对多的关系,如果用多个线程同时满足了线程1结束时的条件,就会同时执行。
以上是我们常用到的线程锁的使用,以及其对应的一些规则。如果有不完整的地方欢迎留言指教。