在我们平常在代码中使用锁的时候,难免遇上死锁这种情况,在这个时候我们可以选择使用同步锁的方式,比较常用的就是NSLock。
在操作递归或者多次数据回调UI线程的时候最容易就出现这种死锁,比方说这样:
//主线程中
NSLock *theLock = [[NSLock alloc] init];
LockObj *obj = [[LockObj alloc] init];//线程dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void(^TestMethod)(int);
TestMethod = ^(int value) {
[theLock lock]; if (value > 0) {
[obj method1];
sleep(5);
TestMethod(value-1);
}
[theLock unlock];
};
TestMethod(5);
});
//线程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
[theLock lock];
[obj method2];
[theLock unlock];
});
非常明显的,在线程1中的递归block中,锁会被多次的lock,所以自己也被阻塞了,死锁就这样造成了。
在一般的情况下,我们总以为添加了NSLock锁,线程就会变得安全,但是这也许可以为自己埋雷。
在翻阅过不少资料之后,广受推荐的仍然是GCD的同步锁。
有关于GCD:
GCD 有两种派发方式:同步派发和异步派发。值得注意的是这里的同步和异步指的是 “任务派发方式”,而非任务的执行方式。
一般我们在数据和UI调度的时候使用最广泛的就是(异步派发):
dispatch_async(queue, block) 做了两件事情:
- 将 block 添加到 queue 队列;
- 直接回到调用线程(不阻塞调用线程)。
dispatch_async (dispatch_get_global_queue, ^{
//搞事情
/*
***......
*/
//回调UI线程:
dispatch_async (dispatch_get_main_queue(),^{
//回到主线程再搞事情
});
});
相对用的较少的就是同步派发:
dispatch_sync(queue, block)
- 将 block 添加到 queue 队列;
- 阻塞调用线程,等待 block() 执行结束,回到调用线程。
实现同步锁可以利用这个特性。
那么如何实现GCD的同步锁呢?
_syncQueue = dispatch_queue_create("com.effectiveObjectiveC.syncQueue", NULL);
- (NSString *)someString
{
__weak NSString *localSomeString;
dispatch_sync(_syncQueue, ^{
localSomeString = _someString;
});
return localSomeString;
}
- (void)setSomeString:(NSString *)someString
{
dispatch_sync(_syncQueue, ^{
_someString = someString;
});
}
通过这样的方式可以实现同步锁的效果,还能避免死锁的情况。
这里只是暂做记录,日后了解更多关于线程和死锁的问题,会陆续补充。
本文参考:
老谭:Objective-C 中不同方式实现锁(一)
老谭:Objective-C 中不同方式实现锁(二)
Effective Objective-C Notes:GCD 实现同步锁