NSRecursiveLock遞歸鎖的使用
NSRecursiveLock遞歸鎖的使用
NSRecursiveLock實(shí)際上定義的是一個(gè)遞歸鎖,這個(gè)鎖可以被同一線程多次請求,而不會(huì)引起死鎖。這主要是用在循環(huán)或遞歸操作中。我們先來看一個(gè)示例:
- NSLock *lock = [[NSLock alloc] init];
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- static void (^RecursiveMethod)(int);
- RecursiveMethod = ^(int value) {
- [lock lock];
- if (value > 0) {
- NSLog(@"value = %d", value);
- sleep(2);
- RecursiveMethod(value - 1);
- }
- [lock unlock];
- };
- RecursiveMethod(5);
- });
這段代碼是一個(gè)典型的死鎖情況。在我們的線程中,RecursiveMethod是遞歸調(diào)用的。所以每次進(jìn)入這個(gè)block時(shí),都會(huì)去加一次鎖,而從第二次開始,由于鎖已經(jīng)被使用了且沒有解鎖,所以它需要等待鎖被解除,這樣就導(dǎo)致了死鎖,線程被阻塞住了。調(diào)試器中會(huì)輸出如下信息:
- value = 5
- *** -[NSLock lock]: deadlock (<NSLock: 0x1700ceee0> '(null)') *** Break on _NSLockError() to debug.
在這種情況下,我們就可以使用NSRecursiveLock。它可以允許同一線程多次加鎖,而不會(huì)造成死鎖。遞歸鎖會(huì)跟蹤它被lock的次數(shù)。每次成功的lock都必須平衡調(diào)用unlock操作。只有所有達(dá)到這種平衡,鎖***才能被釋放,以供其它線程使用。
所以,對上面的代碼進(jìn)行一下改造,
NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
這樣,程序就能正常運(yùn)行了,其輸出如下所示:
- value = 5
- value = 4
- value = 3
- value = 2
- value = 1
NSRecursiveLock除了實(shí)現(xiàn)NSLocking協(xié)議的方法外,還提供了兩個(gè)方法,分別如下:
- // 在給定的時(shí)間之前去嘗試請求一個(gè)鎖
- - (BOOL)lockBeforeDate:(NSDate *)limit
- // 嘗試去請求一個(gè)鎖,并會(huì)立即返回一個(gè)布爾值,表示嘗試是否成功
- - (BOOL)tryLock
- 這兩個(gè)方法都可以用于在多線程的情況下,去嘗試請求一個(gè)遞歸鎖,然后根據(jù)返回的布爾值,來做相應(yīng)的處理。如下代碼所示:
- NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- static void (^RecursiveMethod)(int);
- RecursiveMethod = ^(int value) {
- [lock lock];
- if (value > 0) {
- NSLog(@"value = %d", value);
- sleep(2);
- RecursiveMethod(value - 1);
- }
- [lock unlock];
- };
- RecursiveMethod(5);
- });
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- sleep(2);
- BOOL flag = [lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:1]];
- if (flag) {
- NSLog(@"lock before date");
- [lock unlock];
- } else {
- NSLog(@"fail to lock before date");
- }
- });
在前面的代碼中,我們又添加了一段代碼,增加一個(gè)線程來獲取遞歸鎖。我們在第二個(gè)線程中嘗試去獲取遞歸鎖,當(dāng)然這種情況下是會(huì)失敗的,輸出結(jié)果如下:
- value = 5
- value = 4
- fail to lock before date
- value = 3
- value = 2
- value = 1
另外,NSRecursiveLock還聲明了一個(gè)name屬性,如下:
@property(copy) NSString *name
我們可以使用這個(gè)字符串來標(biāo)識一個(gè)鎖。Cocoa也會(huì)使用這個(gè)name作為錯(cuò)誤描述信息的一部分。
參考
NSRecursiveLock Class Reference