目前最普遍被認知的幾種多線程有:程序員
- pthread
- NSThread
- GCD
- NSOperation
其中經常使用的是後面兩種:GCD 和 NSOperation,這邊文章就是對後兩種常見用法的簡單介紹,以及前兩種 pthread 和 NSThread 的簡單說明。編程
一套通用的多線程API,適用於Unix\Linux\Windows等系統,跨平臺,可移植性強,由純C語言編寫的API,且線程的生命週期須要程序員本身管理,使用難度較大,因此在實際開發中一般不使用,在這裏也不詳細說明。數組
注意:在使用pthread的時候必定要手動把當前線程結束掉。若是有想從底層進行定製多線程的操做,可使用ptherad。安全
基於OC語言由蘋果進行封裝的API,使得其簡單易用,徹底面向對象操做。線程的聲明週期由程序員管理,在實際開發中偶爾使用。bash
- 使用NSThread的init方法顯式建立
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadMethod) object:nil];
//線程名
[thread setName:@"thread"];
//優先級 優先級從0到1
[thread setThreadPriority:0.9];
//啓動
[thread start];
複製代碼
- 使用NSThread類方法顯式建立並啓動線程
[NSThread detachNewThreadSelector:@selector(threadMethod:) toTarget:self withObject:nil];
複製代碼
- 隱式建立並啓動線程
[self performSelectorInBackground:@selector(threadMethod:) withObject:nil];
複製代碼
注意:添加線程的名字、更改優先級等操做,要使用第一種方式來建立線程。由於只有使用NSThread的init方法建立的線程纔會返回具體的線程實例,此方法須要使用start方法來手動啓動線程。多線程
- 啓動
[thread start];
複製代碼
- 阻塞
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
[NSThread sleepForTimeInterval:1];
複製代碼
- 結束(注意:當使用cancel方法時,只是改變了線程的狀態標識,並非結束線程,要配合isCancelled方法進行判斷,退出線程使用)
[[NSThread currentThread] cancel];
if([[NSThread currentThread] isCancelled]) {
[NSThread exit];//執行exit,後邊的語句再也不執行,能夠經過 start 再次啓動線程
}
if([[NSThread currentThread] isCancelled]) {
return;//後邊的語句再也不執行,不能夠經過 start 再次啓動線程
}
複製代碼
線程間通訊,最經常使用的就是開啓子線程進行耗時操做,操做完畢後回到主線程,進行數據賦值以及刷新主線程UI。併發
[self performSelectorOnMainThread:@selector(backToMainThread:) withObject:image waitUntilDone:YES];
複製代碼
[self performSelector:@selector(backToMainThread:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
複製代碼
多線程操做會存在必定的安全隱患。緣由是多線程會存在不一樣線程的資源共享,也就是說咱們可能在同一時刻兩個線程同時操做了某一個變量的值(資源競爭),可是線程的對變量的操做不一樣,致使變量的值出現偏差。app
例如:若是有一個變量x = 100,有兩個線程A和B,A線程取x的值(x=100),B線程取x的值(x=100),B線程給x+1 (x=101),A線程給x+1 (x = 101),B 線程取x的值 (x = 101)或者( x = 102 )。變量出行了偏差。異步
解決方案添加線程鎖,有多種線程鎖,在這裏很少介紹。async
基於C語言編寫由蘋果公司提供的的一套多線程開發解決方案,使用時會以函數形式出現,且大部分函數以dispatch開頭。它會自動利用多核進行併發處理和運算,它能提供系統級別的處理,而再也不侷限於某個進程、線程,線程的生命週期由系統自動管理(建立,調度、運行,銷燬),只須要告訴GCD執行什麼任務,不須要編寫管理線程的代碼。
- 串行列隊(一次執行一個任務)
dispatch_queue_t queue = dispatch_queue_create("10900900",DISPATCH_QUEUE_SERIAL);
複製代碼
- 併發列隊(一次可執行多個任務)
dispatch_queue_t queue = dispatch_queue_create("10900901",DISPATCH_QUEUE_CONCURRENT);
複製代碼
- 全局列隊(本質是一個併發隊列,由系統提供,全部應用程序共享的,方便編程,能夠不用建立就直接使用)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
複製代碼
- 主列隊(專門調度主線程的任務,不開闢新的線程。在主隊列下的任務不論是異步仍是同步都不會開闢新線程,任務只會在主線程順序執行)
dispatch_queue_t queue = dispatch_get_main_queue();
複製代碼
- 同步任務(同步串行和同步併發,任務執行的方式是同樣的,沒有開闢新的線程,全部的任務都是在一條線程裏面執行。)
dispatch_sync(queue, ^{
});
複製代碼
- 異步任務(異步串行和異步併發,任務執行的方式是有區別的,異步串行會開闢一條新的線程,隊列中全部任務按照添加的順序一個一個執行,異步併發會開闢多條線程,至於具體開闢多少條線程,是由系統決定的。)
dispatch_async(queue, ^{
});
複製代碼
- 全部任務都是在當前線程中執行,沒有開啓新的線程(同步方法不具有開啓新線程的能力)
- 同步任務須要等候列隊中的任務執行結束,纔會執行下一個
- 併發列隊能夠開啓多線程,而且能夠同時執行多個任務,可是同步任務沒法建立新線程,因此只有當前一個線程,並且同步任務須要等待列隊中前一任務執行結束才能繼續執行下面的操做,所以任務只能一個一個順序執行
- 和同步任務,併發列隊類似
- 全部任務在當前線程中執行,沒有開啓新的線程
- 任務是按照順序執行的,同步任務,線程須要等待列隊中的任務執行完畢,才能夠開啓新的任務
- 在主線程中調用會出現死鎖,互相等待
- 死鎖緣由:當咱們在主線程中添加這個列隊的時候,添加列隊的這個操做自己就是一個任務,咱們把它看成任務A,這個任務也被添加到了主線程的列隊中。而同步任務,會等待當前列隊中前面的任務執行完畢後接着執行,咱們把添加到主線程中的列隊中的任務稱爲任務B,這就產生了一個矛盾,任務B要執行須要等任務A執行完畢後纔會執行,而任務A執行完畢須要任務B執行結束(由於任務B在任務A中),這就產生了任務互相等待的狀況
- 有幾個異步任務就開啓了幾個新的線程,任務也是同時執行的(異步方法具有開啓新線程的能力,能夠同時執行多個任務)
- 異步執行,當前線程不等待,直接開啓新的線程來執行,在新線程中執行任務(異步任務,添加異步任務的線程不作等待,可繼續執行別的任務)
- 開啓了一條新的線程來執行異步任務(異步任務能夠開啓新線程,串行列隊只能開啓一個線程)
- 線程不會等待任務執行完畢,任務的執行是按照順序來的,每次只有一個任務被執行,任務一個接一個的執行下去
- 沒有開啓新線程,全部任務都是在主線程中執行的(雖然異步任務有開啓新線程的能力,但由於是在主列隊,因此沒法開啓新線程,全部任務都在主線程中執行)
- 因爲只有一個線程可使用,因此全部任務都是按順序一個個執行的,一個完畢,執行下一個
線程間的通信比較經常使用的就是在其餘線程獲取數據,而後返回主線程刷新UI界面
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(queue, ^{
// 異步追加任務
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@",[NSThread currentThread]);
}
// 回到主線程
dispatch_async(mainQueue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"2---%@",[NSThread currentThread]);
});
});
複製代碼
有時咱們須要異步執行兩組操做分別爲A組和B組,當A組完成後,再執行B組操做,所以咱們須要把把兩組操做分割開來。這時可用
dispatch_barrier_async
方法來實現,在添加兩組任務之間添加一個分欄,函數會先把分欄前添加的任務執行完畢以後,在把分欄後的任務添加到隊列中
dispatch_async(queue, blk1);
dispatch_async(queue, blk2);
dispatch_barrier_async(queue, barrierBlk);
dispatch_async(queue, blk3);
dispatch_async(queue, blk4);
複製代碼
柵欄函數也能夠執行隊列上的操做(參數列表中有queue和block),也有對應的
dispatch_barrier_sync
函數。
柵欄函數中傳入的參數隊列必須是由
dispatch_queue_create
方法建立的隊列,不然,與dispatch_async
無異,起不到「柵欄」的做用了,對於dispatch_barrier_sync
也是同理。
柵欄函數以前和以後的操做執行順序都不固定,可是前面三個必然先執行,而後再執行柵欄函數中的操做,最後執行後面的三個
dispatch_barrier_sync
和dispatch_barrier_async
的區別:
- 當柵欄先後添加的都是同步任務,二者沒有區別,按照順序依次執行
- 當柵欄先後添加的是異步任務,sync 會先執行柵欄前的任務,而後不等待柵欄後部任務。async柵欄先後的任務都不等待
因而可知sync和async對於柵欄函數的區別做:
dispatch_barrier_sync
將本身的任務插入到隊列的時候,須要等待本身的任務結束以後纔會繼續插入被寫在它後面的任務,而後執行它們。dispatch_barrier_async
將本身的任務插入到隊列以後,不會等待本身的任務結束,它會繼續把後面的任務插入到隊列,而後等待本身的任務結束後才執行後面任務
。
當遇到要求在指定時間後執行代碼(例如5秒後執行代碼),可用
dispatch_after
來實現,須要注意的是這個並不嚴謹,這個是指在指定時間後,再把代碼加入列隊中去,並非嚴格的在多少時間後開始執行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(second * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// second秒後異步追加任務代碼到主隊列,並開始執行
NSLog(@"after---%@",[NSThread currentThread]); // 打印當前線程
});
複製代碼
在建立單例,或者有代碼要求在整個程序的運行過程當中之執行一次的話可使用GCD中的
dispatch_once
函數,這個函數保證即便在多線程的環境下也能夠保證只調用一次,保證線程安全
- (void)once {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只執行1次的代碼(這裏面默認是線程安全的)
});
}
複製代碼
快速迭代函數
dispatch_apply
按照指定的次數把指定的任務加入到指定的列隊中去,並等待所有的任務執行完畢後,結束
- (void)applyTime:(NSInteger)time {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"apply---begin");
dispatch_apply(time, queue, ^(size_t index) {
NSLog(@"%zd---%@",index, [NSThread currentThread]);
});
NSLog(@"apply---end");
}
複製代碼
注意:因爲是在併發列隊中異步執行,因此裏面的執行完成順序不固定
GCD中的信號量,是一種持有計數的信號,計數爲0時,不可經過,要等待。計數爲1或大於1時,可經過,不需等待。
dispatch_semaphore_create
:建立一個Semaphore並初始化信號的總量dispatch_semaphore_signal
:發送一個信號,讓信號總量加 1dispatch_semaphore_wait
:可使總信號量減1,當信號總量爲0時就會一直等待(阻塞所在線程),不然就能夠正常執行。
- 保持線程同步,使異步執行的任務轉化爲同步執行
- 保護線程安全,爲線程加鎖
例如:當異步執行耗時操做時,須要使用該異步操做的結果進行一些額外的操做,例如:同時進行兩個異步操做A和B,執行B的時候,須要對A的運行結果來進行操做,這個時候就能夠對B加一個信號量,讓B等待,當A執行完畢後,對B操做發送信號,繼續執行B操做
- (void)semaphoreSync {
NSLog(@"semaphore---begin");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//建立初始信號量 爲 0 ,阻塞全部線程
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block int number = 0;
dispatch_async(queue, ^{
// 追加任務A
[NSThread sleepForTimeInterval:2]; // 模擬耗時操做
NSLog(@"1---%@",[NSThread currentThread]); // 打印當前線程
number = 100;
// 執行完線程,信號量加 1,信號總量從 0 變爲 1
dispatch_semaphore_signal(semaphore);
});
//原任務B
////若計數爲0則一直等待,直到接到總信號量變爲 >0 ,繼續執行後續代碼
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore---end,number = %d",number);
}
複製代碼
例如:線程安全方面:若每一個線程中對全局變量、靜態變量只有讀操做,而無寫操做,通常來講,這個全局變量是線程安全的;如有多個線程同時執行寫操做(更改變量),通常都須要考慮線程同步,不然的話就可能影響線程安全。(可理解爲線程 A 和 線程 B 一塊配合,A 執行到必定程度時要依靠線程 B 的某個結果,因而停下來,示意 B 運行;B 依言執行,再將結果給 A;A 再繼續操做。)
有時候會遇到須要異步執行兩個耗時任務,而後當兩個任務都執行完畢後,在回到主線程執行任務,這時候咱們能夠用GCD的隊列組的功能來實現這個要求
- 調用隊列組的
dispatch_group_async
先把任務放到列隊中,而後把列隊放入到列隊組中。也可用dispatch_group_enter
和dispatch_group_leave
兩個組合來實現dispatch_group_async
dispatch_group_enter
:加入,一個任務追加到group,執行一次,至關於group中的未執行任務加1dispatch_group_leave
:離開,一個任務離開了group,執行一次,至關於group中的未執行任務減1
當group中的未執行完畢的任務數爲0的時候纔會執行
dispatch_group_notify
中的任務,以及不會使dispatch_group_wait
堵塞當前線程(相似於信號量)
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_enter(group);
dispatch_sync(queue, ^{
// 追加任務A
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操做
NSLog(@"1---%@",[NSThread currentThread]); // 打印當前線程
}
dispatch_group_leave(group);
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
複製代碼
- 調用隊列組的
dispatch_group_notify
回到指定線程執行任務(監聽group中的全部任務的完成狀態,當全部任務都完成後,把notify中的任務添加到group中,並執行任務)
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務A
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操做
NSLog(@"1---%@",[NSThread currentThread]); // 打印當前線程
}
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務B
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操做
NSLog(@"2---%@",[NSThread currentThread]); // 打印當前線程
}
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 追加任務Main
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操做
NSLog(@"main---%@",[NSThread currentThread]); // 打印當前線程
}
});
複製代碼
- 調用隊列組的
dispatch_group_wait
回到當前線程繼續向下執行(暫停當前線程中的操做,阻塞當前線程,執行wait中的group操做,執行完後,繼續執行當前線程中的操做)
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務A
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操做
NSLog(@"2---%@",[NSThread currentThread]); // 打印當前線程
}
});
//執行A任務,執行完成後繼續執行該線程後續任務
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"current---%@",[NSThread currentThread]);
複製代碼
基於OC語言的API,底層是GCD,增長了一些更加簡單易用的功能,使用更加面向對象。線程生命週期由系統自動管理,在實際開發中常用。
- 能夠添加代完成的代碼塊,在操做完成後執行
- 添加操做之間的依賴關係,控制執行順序
- 設定操做執行的優先級
- 能夠很方便的取消一個操做的執行
- 使用KVO觀察對象操做執行狀態的更改:isExecuteing(執行) isFinished(結束) isCancelled(取消)
- 線程中執行的操做代碼
- 與GCD不一樣,GCD是放在block塊中。在
NSOperation
中,可使用它的子類NSInvocationOperation
,NSBlockOperation
來實現,也能夠自定義子來封裝操做
- 這個列隊不一樣於GCD中的列隊中先進先出的原則,這裏的列隊對於添加到列隊中的操做首先進入準備就緒的狀態(這個根據操做之間的依賴關係來決定),而後進能夠開始執行的操做的執行順序要先按照操做之間的相對的優先級來決定,而後再根據進入的順序決定執行順序
- 操做的隊列經過設置 最大併發操做數 (
maxConcurrentOperationCount
)來控制併發和串行NSOperationQueue
提供了兩種不一樣的列隊,主列隊和自定義列隊。主列隊運行在主線程之上,自定義列隊在後臺執行。
NSInvocationOperation
-(void)useNSInvocationOperation{
NSInvocationOperation * op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationTest) object:nil];
[op start];
}
複製代碼
是在沒有使用NSOperationQueue的狀況下,在主線程中單獨使用子類執行一個操做,操做是在當前線程執行的,並無開啓新線程,想要在其餘線程來執行這個操做的話可使用:
[NSThread detachNewThreadSelector:@selector(useNSInvocationOperation) toTarget:self withObject:nil];
複製代碼
NSBlockOperation
-(void)useNSBlockOperation{
NSBlockOperation * op = [NSBlockOperation blockOperationWithBlock:^ {
for (NSInteger i = 0 ; i < 2 ; i ++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@", [NSThread currentThread]);
}
}];
[op start];
}
複製代碼
和上一個同樣也是在當前線程調用,想要在其餘線程使用的話能夠:
[NSThread detachNewThreadSelector:@selector(useNSBlockOperation) toTarget:self withObject:nil];
複製代碼
注意:NSBlockOperation有方法能夠添加額外的操做addExecutionBlock:
[op addExecutionBlock:^{
for (NSInteger i = 0 ; i < 2 ; i ++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2---%@", [NSThread currentThread]);
}
}];
複製代碼
能夠在不一樣的線程中併發執行,只有全部添加的操做所有完成,才視爲這個block操做完成。通常狀況下,一個operation對象若是封裝了多個操做,是否開啓新線程來執行這些操做,取決於操做的個數,由系統來決定是否開啓新線程。
NSOperation
自定義的子類能夠經過重寫 main 或者 start 方法來自定義操做對象 重寫 main 方法比較簡單,不用管理狀態屬性 isExecuting 等。
-(void)useCustomOperation{
JMOperation * op = [[JMOperation alloc] init];
[op start];
}
複製代碼
操做的具體實現寫在重寫的類中,方便管理和統一修改。
NSOperationQueue * queue = [NSOperationQueue mainQueue];
複製代碼
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
複製代碼
-(void)createCustomQueue{
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
NSInvocationOperation * op_1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationTest) object:nil];
NSBlockOperation * op_2 = [NSBlockOperation blockOperationWithBlock:^ {
for (NSInteger i = 0 ; i < 2 ; i ++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2---%@", [NSThread currentThread]);
}
}];
[op_2 addExecutionBlock:^{
for (NSInteger i = 0 ; i < 2 ; i ++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"3---%@", [NSThread currentThread]);
}
}];
[queue addOperation:op_1]; // 操做加入隊列
[queue addOperation:op_2];
}
複製代碼
-(void)createMainQueue{
NSOperationQueue * queue = [NSOperationQueue mainQueue];
// 隊列加入block方法
[queue addOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操做
NSLog(@"1---%@", [NSThread currentThread]); // 打印當前線程
}
}];
[queue addOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操做
NSLog(@"2---%@", [NSThread currentThread]); // 打印當前線程
}
}];
}
複製代碼
關鍵詞(
maxConcurrentOperationCount
),最大併發操做數,用來控制一個列隊中能夠有多少個操做同時併發執行
注意:這個數值控制的並非併發線程的數量,而是一個隊列中同時能併發執行的最大操做數,一個操做並不是只能在一個線程中運行。
開啓線程的數量由系統決定,沒法人爲管理。
queue.maxConcurrentOperationCount = 4;
- 默認爲-1,表示不限制,能夠進行併發執行
- 值爲1時,表示只可串行執行
- 大於1時,表示併發執行,不可超過系統限制
NSOperation的獨有功能,操做依賴,經過操做依賴,咱們能夠根據設置的依賴關係,很方便的控制操做的執行順序,NSOperation提供3個接口供咱們管理和查看依賴
[op_1 addDependency:op_2]
;[op_1 removeDependency:op_2]
;@property (readonly, copy) NSArray<NSOperation *> *dependencies
;上面的三個操做分別是添加依賴,移除依賴,獲取當前操做所依賴的全部操做的數組
NSOperation提供了 queuePriority (優先級)屬性。queuePriority 屬性適用於同一操做列隊中的操做,不適用於不一樣操做列隊中的操做。默認狀況下,全部操做對象優先級都是 NSOperationQueuePriorityNormal 。可是咱們能夠經過setQueuePriority: 方法來改變當前操做在同一個列隊中的優先級:
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
複製代碼
注意:
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
for (NSInteger i = 0 ; i < 2 ; i ++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2---%@", [NSThread currentThread]);
}
}];
//返回主線程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// 進行一些 UI 刷新等操做
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時操做
NSLog(@"2---%@", [NSThread currentThread]); // 打印當前線程
}
}];
複製代碼
經過
[NSOperationQueue mainQueue]
函數返回主線程的操做
多線程有多種枷鎖方式來保護線程安全,經常使用的是NSLock,經過NSLock來給進程加鎖,解鎖。加鎖後其餘進程沒法再訪問這個方法和屬性
NSLock * lock = [[NSLock alloc] init]
;[lock lock]
;[lock unlock]
;初始化lock方法,加鎖,解鎖方法
某個A線程調用lock方法,這樣,nslock將被上鎖,能夠執行被鎖住的關鍵方法,完成後A線程調用unlock方法解鎖。若是在A線程調用unlock方法以前,有B線程須要調用被鎖住的關鍵方法,那麼將沒法訪問,一直等待,直到A線程調用了unlock方法。
其餘還有多種鎖的方式,如:自旋鎖,互斥鎖,遞歸鎖,條件鎖,讀寫鎖等,具體進程鎖在下篇文章再來介紹。iOS開發基礎——線程安全(進程鎖)