在我們平常在代碼中使用鎖的時候,難免遇上死鎖這種情況,在這個時候我們可以選擇使用同步鎖的方式,比較常用的就是NSLock。
在操作遞歸或者多次數(shù)據(jù)回調(diào)UI線程的時候最容易就出現(xiàn)這種死鎖,比方說這樣:
//主線程中
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的同步鎖。
有關(guān)于GCD:
GCD 有兩種派發(fā)方式:同步派發(fā)和異步派發(fā)。值得注意的是這里的同步和異步指的是 “任務(wù)派發(fā)方式”,而非任務(wù)的執(zhí)行方式。
一般我們在數(shù)據(jù)和UI調(diào)度的時候使用最廣泛的就是(異步派發(fā)):
dispatch_async(queue, block) 做了兩件事情:
- 將 block 添加到 queue 隊列;
- 直接回到調(diào)用線程(不阻塞調(diào)用線程)。
dispatch_async (dispatch_get_global_queue, ^{
//搞事情
/*
***......
*/
//回調(diào)UI線程:
dispatch_async (dispatch_get_main_queue(),^{
//回到主線程再搞事情
});
});
相對用的較少的就是同步派發(fā):
dispatch_sync(queue, block)
- 將 block 添加到 queue 隊列;
- 阻塞調(diào)用線程,等待 block() 執(zhí)行結(jié)束,回到調(diào)用線程。
實現(xiàn)同步鎖可以利用這個特性。
那么如何實現(xiàn)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;
});
}
通過這樣的方式可以實現(xiàn)同步鎖的效果,還能避免死鎖的情況。
這里只是暫做記錄,日后了解更多關(guān)于線程和死鎖的問題,會陸續(xù)補充。
本文參考:
老譚:Objective-C 中不同方式實現(xiàn)鎖(一)
老譚:Objective-C 中不同方式實現(xiàn)鎖(二)
Effective Objective-C Notes:GCD 實現(xiàn)同步鎖