1 Objective-C中不一樣方式實現鎖(二) 2 3 在上一文中,咱們已經討論過用Objective-C鎖幾種實現(跳轉地址),也用代碼實際的演示瞭如何經過構建一個互斥鎖來實現多線程的資源共享及線程安全,今天咱們繼續討論鎖的一些高級用法。 4 5 1.NSRecursiveLock遞歸鎖 6 7 平時咱們在代碼中使用鎖的時候,最容易犯的一個錯誤就是形成死鎖,而容易形成死鎖的一種情形就是在遞歸或循環中,以下代碼: 8 9 1 10 2 11 3 12 4 13 5 14 6 15 7 16 8 17 9 18 10 19 11 20 12 21 13 22 14 23 15 24 16 25 17 26 18 27 19 28 20 29 21 30 22 31 23 32 24 33 25 34 26 35 27 36 28 37 29 38 30 39 //主線程中 40 NSLock *theLock = [[NSLock alloc] init]; 41 TestObj *obj = [[TestObj alloc] init]; 42 43 //線程1 44 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 45 46 static void(^TestMethod)(int); 47 TestMethod = ^(int value) 48 { 49 [theLock lock]; 50 if (value > 0) 51 { 52 [obj method1]; 53 sleep(5); 54 TestMethod(value-1); 55 } 56 [theLock unlock]; 57 }; 58 59 TestMethod(5); 60 }); 61 62 //線程2 63 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 64 sleep(1); 65 [theLock lock]; 66 [obj method2]; 67 [theLock unlock]; 68 }); 69 以上的代碼中,就是一種典型的死鎖狀況,由於在線程1中的遞歸block中,鎖會被屢次的lock,因此本身也被阻塞了,因爲以上的代碼很是的簡短,因此很容易能識別死鎖,但在較爲複雜的代碼中,就不那麼容易發現了,那麼如何在遞歸或循環中正確的使用鎖呢?此處的theLock若是換用NSRecursiveLock對象,問題便獲得解決了,NSRecursiveLock類定義的鎖能夠在同一線程屢次lock,而不會形成死鎖。遞歸鎖會跟蹤它被多少次lock。每次成功的lock都必須平衡調用unlock操做。只有全部的鎖住和解鎖操做都平衡的時候,鎖才真正被釋放給其餘線程得到。 70 71 2.NSConditionLock條件鎖 72 73 當咱們在使用多線程的時候,有時一把只會lock和unlock的鎖未必就能徹底知足咱們的使用。由於普通的鎖只能關心鎖與不鎖,而不在意用什麼鑰匙才能開鎖,而咱們在處理資源共享的時候,多數狀況是隻有知足必定條件的狀況下才能打開這把鎖: 74 75 1 76 2 77 3 78 4 79 5 80 6 81 7 82 8 83 9 84 10 85 11 86 12 87 13 88 14 89 15 90 16 91 17 92 18 93 19 94 20 95 //主線程中 96 NSConditionLock *theLock = [[NSConditionLock alloc] init]; 97 98 //線程1 99 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 100 for (int i=0;i<=2;i++) 101 { 102 [theLock lock]; 103 NSLog(@"thread1:%d",i); 104 sleep(2); 105 [theLock unlockWithCondition:i]; 106 } 107 }); 108 109 //線程2 110 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 111 [theLock lockWhenCondition:2]; 112 NSLog(@"thread2"); 113 [theLock unlock]; 114 }); 115 在線程1中的加鎖使用了lock,因此是不須要條件的,因此順利的就鎖住了,但在unlock的使用了一個整型的條件,它能夠開啓其它線程中正在等待這把鑰匙的臨界地,而線程2則須要一把被標識爲2的鑰匙,因此當線程1循環到最後一次的時候,才最終打開了線程2中的阻塞。但即使如此,NSConditionLock也跟其它的鎖同樣,是須要lock與unlock對應的,只是lock,lockWhenCondition:與unlock,unlockWithCondition:是能夠隨意組合的,固然這是與你的需求相關的。 116 117 3.NSDistributedLock分佈式鎖 118 119 以上全部的鎖都是在解決多線程之間的衝突,但若是趕上多個進程或多個程序之間須要構建互斥的情景該怎麼辦呢?這個時候咱們就須要使用到NSDistributedLock了,從它的類名就知道這是一個分佈式的Lock,NSDistributedLock的實現是經過文件系統的,因此使用它才能夠有效的實現不一樣進程之間的互斥,但NSDistributedLock並不是繼承於NSLock,它沒有lock方法,它只實現了tryLock,unlock,breakLock,因此若是須要lock的話,你就必須本身實現一個tryLock的輪詢,下面經過代碼簡單的演示一下吧: 120 121 程序A: 122 123 1 124 2 125 3 126 4 127 5 128 6 129 7 130 8 131 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 132 lock = [[NSDistributedLock alloc] initWithPath:@"/Users/mac/Desktop/earning__"]; 133 [lock breakLock]; 134 [lock tryLock]; 135 sleep(10); 136 [lock unlock]; 137 NSLog(@"appA: OK"); 138 }); 139 程序B: 140 141 1 142 2 143 3 144 4 145 5 146 6 147 7 148 8 149 9 150 10 151 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 152 lock = [[NSDistributedLock alloc] initWithPath:@"/Users/mac/Desktop/earning__"]; 153 154 while (![lock tryLock]) { 155 NSLog(@"appB: waiting"); 156 sleep(1); 157 } 158 [lock unlock]; 159 NSLog(@"appB: OK"); 160 }); 161 先運行程序A,而後當即運行程序B,根據打印你能夠清楚的發現,當程序A剛運行的時候,程序B一直處於等待中,當大概10秒事後,程序B便打印出了appB:OK的輸出,以上便實現了兩上不一樣程序之間的互斥。/Users/mac/Desktop/earning__是一個文件或文件夾的地址,若是該文件或文件夾不存在,那麼在tryLock返回YES時,會自動建立該文件/文件夾。在結束的時候該文件/文件夾會被清除,因此在選擇的該路徑的時候,應該選擇一個不存在的路徑,以防止誤刪了文件。