多線程開發是平常開發任務中不可缺乏的一部分,在iOS開發中經常使用到的多線程開發技術有
GCD、NSOperation、NSThread
,本文主要講解多線系列文章中關於NSOperation
的相關知識和使用詳解。html
GCD
對於iOS開發者來講並不陌生,在實際開發中咱們會常常用到GCD進行多線程的處理,那麼GCD是什麼呢?編程
Grand Central Dispatch(GCD) 是 Apple 開發的一個多核編程的較新的解決方法。它主要用於優化應用程序以支持多核處理器以及其餘對稱多處理系統。它是一個在線程池模式的基礎上執行的併發任務。在 Mac OS X 10.6 雪豹中首次推出,也可在 iOS 4 及以上版本使用。安全
GCD有着很明顯的優點,正是這些優點才使得GCD在處理多線程問題有着舉足輕重的地位。bash
- GCD是apple爲多核的並行運算提出的解決方案。
- GCD能較好的利用CPU內核資源。
- GCD不須要開發者去管理線程的生命週期。
- 使用簡便,開發者只須要告訴GCD執行什麼任務,並不須要編寫任何線程管理代碼。
相信不少初級開發者會對GCD任務和隊列之間的關係理解含糊不清,實際上隊列只是提供了保存任務的容器。爲了更好的理解GCD,頗有必要先了解任務和隊列的概念。網絡
任務就是須要執行的操做,是GCD中放在block中在線程中執行的那段代碼。任務的執行的方式有同步執行和異步執行兩中執行方式。二者的主要區別是是否等待隊列的任務執行結束,以及是否具有開啓新線程的能力。多線程
隊列:隊列是一種特殊的線性表,隊列中容許插入操做的一端稱爲隊尾,容許刪除操做的一端稱爲隊頭,是一種先進先出的結構。在GCD裏面隊列是指執行任務的等待隊列,是用來存聽任務的。按照隊列的結構特性,新任務老是插入在隊列的末尾,而任務的執行老是從隊列的對頭輸出,每讀取一個任務,則從隊列中釋放一個任務。GCD的隊列分爲串行隊列和併發隊列兩種,二者都符合 FIFO(先進先出)的原則。二者的主要區別是:執行順序不一樣,以及開啓線程數不一樣。併發
二者之間區別以下圖所示: app
GCD的使用很簡單,首先建立一個隊列,而後向隊列中追加任務,系統會根據任務的類型執行任務。異步
dispatch_queue_create
方法傳入相對應的參數即可。這個方法有兩個參數:DISPATCH_QUEUE_SERIAL
表示串行隊列,DISPATCH_QUEUE_CONCURRENT
表示併發隊列。// 建立串行隊列
dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_SERIAL);
// 建立併發隊列
dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_CONCURRENT);
複製代碼
dispatch_get_global_queue
方法來獲取全局併發隊列。這個方法須要傳入兩個參數。DISPATCH_QUEUE_PRIORITY_HIGH
、DISPATCH_QUEUE_PRIORITY_LOW
、DISPATCH_QUEUE_PRIORITY_BACKGROUND
、DISPATCH_QUEUE_PRIORITY_DEFAULT
四個選項,通常用 DISPATCH_QUEUE_PRIORITY_DEFAULT
。// 獲取全局併發隊列
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
複製代碼
dispatch_get_main_queue
方法獲取,全部放在主隊列中的任務都會在主線程中執行。主隊列是一種串行隊列。// 主隊
dispatch_queue_t mainQueue = dispatch_get_main_queue();
複製代碼
GCD調用dispatch_sync
建立同步任務,調用dispatch_async
建立異步任務。任務的內容都是在block代碼塊中。async
//異步任務
dispatch_async(queue, ^{
//異步執行的代碼
});
//同步任務
dispatch_sync(queue, ^{
//同步執行的代碼
});
複製代碼
建立的任務須要放在隊列中去執行,同時考慮到主隊列的特殊性,那麼在不考慮嵌套任務的狀況下就會存在同步任務+串行隊列、同步任務+併發隊列、異步任務+串行隊列、異步任務+併發隊列、主隊列+同步任務、主隊列+異步任務六種組合,下面咱們來分析下這幾種組合。
下面咱們來看看各類組合之間的使用。
- (void)syncTaskWithSerial {
NSLog(@"currentThread:%@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"currentThread-1:%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"currentThread-2:%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"currentThread-3:%@", [NSThread currentThread]);
});
}
複製代碼
打印結果: 2020-03-12 21:34:25.807965+0800 ThreadDemo[51144:6948582] currentThread:<NSThread: 0x600001739100>{number = 1, name = main} 2020-03-12 21:34:25.808231+0800 ThreadDemo[51144:6948582] currentThread-1:<NSThread: 0x600001739100>{number = 1, name = main} 2020-03-12 21:34:25.808467+0800 ThreadDemo[51144:6948582] currentThread-2:<NSThread: 0x600001739100>{number = 1, name = main} 2020-03-12 21:34:25.808669+0800 ThreadDemo[51144:6948582] currentThread-3:<NSThread: 0x600001739100>{number = 1, name = main}
從上面代碼運行的結果能夠看出,並無開啓新的線程,任務是按順序執行的。
- (void)syncTaskWithConcurrent {
NSLog(@"current thread:%@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
NSLog(@"current thread-1:%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"current thread-2:%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"current thread-3:%@", [NSThread currentThread]);
});
}
複製代碼
打印結果: 2020-03-12 21:39:45.931001+0800 ThreadDemo[51225:6953218] current thread:<NSThread: 0x600002abe0c0>{number = 1, name = main} 2020-03-12 21:39:45.931259+0800 ThreadDemo[51225:6953218] current thread-1:<NSThread: 0x600002abe0c0>{number = 1, name = main} 2020-03-12 21:39:45.931442+0800 ThreadDemo[51225:6953218] current thread-2:<NSThread: 0x600002abe0c0>{number = 1, name = main} 2020-03-12 21:39:45.931606+0800 ThreadDemo[51225:6953218] current thread-3:<NSThread: 0x600002abe0c0>{number = 1, name = main}
從上面代碼運行的結果能夠看出,同步任務不會開啓新的線程,雖然任務在併發隊列中,可是系統只默認開啓了一個主線程,沒有開啓子線程,因此任務串行執行。
- (void)asyncTaskWithSeria{
NSLog(@"current thread:%@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"current thread-1:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"current thread-2:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"current thread-3:%@", [NSThread currentThread]);
});
NSLog(@"4");
}
複製代碼
打印結果: 2020-03-12 21:44:22.369058+0800 ThreadDemo[51283:6957598] current thread:<NSThread: 0x6000031891c0>{number = 1, name = main} 2020-03-12 21:44:22.369279+0800 ThreadDemo[51283:6957598] 4 2020-03-12 21:44:22.369346+0800 ThreadDemo[51283:6958684] current thread-1:<NSThread: 0x6000031acb80>{number = 7, name = (null)} 2020-03-12 21:44:22.369511+0800 ThreadDemo[51283:6958684] current thread-2:<NSThread: 0x6000031acb80>{number = 7, name = (null)} 2020-03-12 21:44:22.369675+0800 ThreadDemo[51283:6958684] current thread-3:<NSThread: 0x6000031acb80>{number = 7, name = (null)}
從上面代碼運行的結果能夠看出,開啓了一個新的線程,說明異步任務具有開啓新的線程的能力,可是因爲任務是在串行隊列中執行的,因此任務是順序執行的。
- (void)asyncTaskWithConcurrent{
NSLog(@"current thread:%@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"current thread-1:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"current thread-2:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"current thread-3:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"current thread-4:%@", [NSThread currentThread]);
});
}
複製代碼
打印結果: 2020-03-12 21:59:36.511770+0800 ThreadDemo[51635:6976397] current thread:<NSThread: 0x60000024ed00>{number = 1, name = main} 2020-03-12 21:59:36.512015+0800 ThreadDemo[51635:6976575] current thread-2:<NSThread: 0x600000214ec0>{number = 5, name = (null)} 2020-03-12 21:59:36.512011+0800 ThreadDemo[51635:6976577] current thread-1:<NSThread: 0x600000215700>{number = 4, name = (null)} 2020-03-12 21:59:36.512028+0800 ThreadDemo[51635:6976580] current thread-3:<NSThread: 0x60000021f2c0>{number = 6, name = (null)} 2020-03-12 21:59:36.512035+0800 ThreadDemo[51635:6976578] current thread-4:<NSThread: 0x60000023b340>{number = 7, name = (null)}
從上面代碼的運行結果能夠看出,生成了多個線程,而且任務是隨機執行(併發執行)的。
-(void)syncTaskWithMain{
NSLog(@"currentThread---%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"1---%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2---%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3---%@",[NSThread currentThread]);
});
NSLog(@"4");
}
複製代碼
打印結果: 2020-03-12 22:05:01.689594+0800 ThreadDemo[51754:6982402] currentThread---<NSThread: 0x600003eaed00>{number = 1, name = main} (lldb)
很明顯上面這段代碼運行崩潰了,這是由於咱們在主線程中執行 syncTaskWithMain
方法,至關於把 syncTaskWithMain
任務放到了主線程的隊列中。而 同步執行
會等待當前隊列中的任務執行完畢,纔會接着執行。那麼當咱們把 任務 1
追加到主隊列中,任務 1
就在等待主線程處理完 syncTaskWithMain
任務。而syncMain
任務須要等待任務 1
執行完畢,這樣就造成了相互等待的狀況,產生了死鎖。
-(void)asyncTaskWithMain{
NSLog(@"currentThread---%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"1---%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2---%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3---%@",[NSThread currentThread]);
});
NSLog(@"4");
}
複製代碼
打印結果: 2020-03-12 22:09:49.285203+0800 ThreadDemo[51832:6986908] currentThread---<NSThread: 0x600000aff3c0>{number = 1, name = main} 2020-03-12 22:09:49.285539+0800 ThreadDemo[51832:6986908] 4 2020-03-12 22:09:49.326310+0800 ThreadDemo[51832:6986908] 1---<NSThread: 0x600000aff3c0>{number = 1, name = main} 2020-03-12 22:09:49.326749+0800 ThreadDemo[51832:6986908] 2---<NSThread: 0x600000aff3c0>{number = 1, name = main} 2020-03-12 22:09:49.326988+0800 ThreadDemo[51832:6986908] 3---<NSThread: 0x600000aff3c0>{number = 1, name = main}
從上面代碼的運行結果能夠看出,雖然是異步任務,可是並無開啓新的線程,任然是在主線程中執行,而且任務是順序執行的。
關於任務的嵌套使用有多種狀況,這裏作一個簡單的總結,
區別 | 『異步執行+併發隊列』嵌套『同一個併發隊列』 | 『同步執行+併發隊列』嵌套『同一個併發隊列』 | 『異步執行+串行隊列』嵌套『同一個串行隊列』 | 『同步執行+串行隊列』嵌套『同一個串行隊列』 |
---|---|---|---|---|
同步 | 沒有開啓新的線程,串行執行任務 | 沒有開啓新線程,串行執行任務 | 死鎖卡住不執行 | 死鎖卡住不執行 |
異步 | 有開啓新線程,併發執行任務 | 有開啓新線程,併發執行任務 | 有開啓新線程(1 條),串行執行任務 | 有開啓新線程(1 條),串行執行任務 |
對於**『異步執行+串行隊列』嵌套『同一個串行隊列』**形成死鎖的狀況請看以下代碼:
dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_SERIAL);
//異步任務A
dispatch_async(queue, ^{
//同步任務B
dispatch_sync(queue, ^{
NSLog(@"任務C---%@",[NSThread currentThread]);
});
});
複製代碼
首先異步任務A進入到隊列中,同步任務B對於異步任務A來講是代碼執行部分,C對於同步任務B來講是代碼執行部分,由於是在串行隊列中,任務是串行執行的,根據隊列先進先出原則,首先須要把任務A取出執行,即執行B的部分,可是B依賴C的執行,而C等待着B執行完成後執行,這樣就造成了一個相互等待,形成死鎖卡死。
**同步執行+串行隊列』嵌套『同一個串行隊列』**形成死鎖的狀況同理分析。
在 iOS 開發過程當中,咱們在主線程進行UI刷新,把圖片下載、文件上傳、網絡請求等一些耗時的操做放在其餘的線程,當這些耗時的操做完成後須要將數據同步給UI,就須要回到主線程刷新UI,那麼就要用到線程之間的通信。GCD提供了很是簡便的方法進行線程間的通信。
- (void)communication {
dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"1---%@",[NSThread currentThread]);
// 模擬耗時操做
sleep(2);
// 回到主線程
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"2---%@",[NSThread currentThread]); // 打印當前線程
});
});
}
複製代碼
打印結果 2020-03-13 00:06:07.632014+0800 ThreadDemo[53480:7074047] 1---<NSThread: 0x60000063e480>{number = 5, name = (null)} 2020-03-13 00:06:09.633041+0800 ThreadDemo[53480:7073841] 2---<NSThread: 0x60000061a180>{number = 1, name = main}
從上面代碼運行的結果能夠看出,1是在子線程中執行的,隔2秒後打印2,2是在主線程中執行的。
GCD中提供了諸多的函數方法供開發者調用,咱們一塊兒來看下這些方法的使用。
有的時候咱們須要異步執行兩組操做,等待第一組執行完成後纔回去執行第二組操做,這個時候柵欄方法就起做用了。 柵欄方法(dispatch_barrier_async
或dispatch_barrier_sync
)會等前邊追加到隊列中的任務執行完畢後,再將制定的任務追加到隊列中,而後等到dispatch_barrier_async
或dispatch_barrier_sync
方法追加的任務執行完畢後纔會去執行後邊追加到隊列中的任務,簡單來講dispatch_barrier_async
或dispatch_barrier_sync
將異步任務分紅了兩個組,執行完第一組後,再執行本身,而後執行隊列中剩餘的任務。惟一不一樣的是dispatch_barrier_async
不會阻塞線程。
看以下代碼:
- (void)barrierTask {
dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"start");
dispatch_async(queue, ^{
NSLog(@"currentThread-1:%@", [NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"currentThread-2:%@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:2];
});
NSLog(@"pause");
dispatch_async(queue, ^{
NSLog(@"currentThread-3:%@", [NSThread currentThread]);
});
NSLog(@"end");
}
複製代碼
打印結果 2020-03-13 00:31:53.247514+0800 ThreadDemo[54101:7100220] start 2020-03-13 00:31:53.247730+0800 ThreadDemo[54101:7100220] pause 2020-03-13 00:31:53.247809+0800 ThreadDemo[54101:7100396] currentThread-1:<NSThread: 0x600003b8db00>{number = 5, name = (null)} 2020-03-13 00:31:53.247883+0800 ThreadDemo[54101:7100220] end 2020-03-13 00:31:53.247991+0800 ThreadDemo[54101:7100396] currentThread-2:<NSThread: 0x600003b8db00>{number = 5, name = (null)} 2020-03-13 00:31:55.250622+0800 ThreadDemo[54101:7100396] currentThread-3:<NSThread: 0x600003b8db00>{number = 5, name = (null)}
從上面的代碼運行結果能夠看出,start、pause、end都是在2執行答應的,說明dispatch_barrier_async並無阻塞線程,3是在2打印兩秒後打印的。
若是把dispatch_barrier_async換成dispatch_barrier_sync打印結果會是怎麼樣呢?
- (void)barrierTask {
dispatch_queue_t queue = dispatch_queue_create("com.shen.thread.demo", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"start");
dispatch_async(queue, ^{
NSLog(@"currentThread-1:%@", [NSThread currentThread]);
});
dispatch_barrier_sync(queue, ^{
NSLog(@"currentThread-2:%@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:2];
});
NSLog(@"pause");
dispatch_async(queue, ^{
NSLog(@"currentThread-3:%@", [NSThread currentThread]);
});
NSLog(@"end");
}
複製代碼
打印結果 2020-03-13 00:35:01.460109+0800 ThreadDemo[54171:7103212] start 2020-03-13 00:35:01.460408+0800 ThreadDemo[54171:7103379] currentThread-1:<NSThread: 0x600002508540>{number = 6, name = (null)} 2020-03-13 00:35:01.460588+0800 ThreadDemo[54171:7103212] currentThread-2:<NSThread: 0x600002570880>{number = 1, name = main} 2020-03-13 00:35:03.461678+0800 ThreadDemo[54171:7103212] pause 2020-03-13 00:35:03.462012+0800 ThreadDemo[54171:7103212] end 2020-03-13 00:35:03.462145+0800 ThreadDemo[54171:7103379] currentThread-3:<NSThread: 0x600002508540>{number = 6, name = (null)}
從上面代碼運行的結果能夠看出,pause和end是在2以後打印的,說明dispatch_barrier_sync阻塞了線程,須要等待dispatch_barrier_sync執行完成後纔會日後執行。
延時執行任務相信對於iOS開發者來講並不陌生,咱們常常遇到須要在延後指定之間後執行某個操做的需求,那麼這種需求用GCD來實現是很方便的。GCD的延時執行的函數是dispatch_after
。須要注意的是:dispatch_after
方法並非在指定時間以後纔開始執行處理,而是在指定時間以後將任務追加到主隊列中。嚴格來講,這個時間並非絕對準確的,但想要大體延遲執行任務,dispatch_after
方法是頗有效的。
-(void)afterTask{
NSLog(@"begin");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"after---%@",[NSThread currentThread]);
});
}
複製代碼
打印結果 2020-03-13 00:43:47.787769+0800 ThreadDemo[54378:7111012] begin 2020-03-13 00:43:50.788086+0800 ThreadDemo[54378:7111012] after---<NSThread: 0x60000042ddc0>{number = 1, name = main}
從上面代碼的運行結果能夠看出afer是在begin打印後3秒纔打印的。
GCD提供了只執行一次的方法dispatch_once
,這個方法在咱們建立單例的時候回常常用到。dispatch_once
方法能夠保證一段代碼在程序運行過程當中只被調用一次,並且在多線程環境下能夠保證線程安全。
+ (instancetype)shareInstance{
static Test *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [Test alloc]init];
});
return instance;
}
複製代碼
調度組簡單來講就是把異步執行的任務進行分組,等待全部的分組任務都執行完畢後再回到指定的線程執行任務。調用組使用dispatch_group_create
來建立一個分組,dispatch_group_async
方法先把任務添加到隊列中,而後將隊列方到調度組中,或者也可使用dispatch_group_enter
和dispatch_group_leave
捉對實現將隊列添加到調度組。調用dispatch_group_notify
方法回到指定線程執行任務,或者調用dispatch_group_wait
阻塞當前線程。
- (void)groupNotifyTest{
NSLog(@"current thread:%@", [NSThread currentThread]);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"thread-1:%@", [NSThread currentThread]);
});
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"thread-2:%@", [NSThread currentThread]);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"thread-3:%@", [NSThread currentThread]);
NSLog(@"group-end");
});
//會阻塞線程,
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"dispatch_group_wait後繼續執行任務");
}
複製代碼
打印結果: 2020-03-14 23:58:09.871023+0800 ThreadDemo[77370:8259501] current thread:<NSThread: 0x6000019fe480>{number = 1, name = main} 2020-03-14 23:58:11.874345+0800 ThreadDemo[77370:8260290] thread-2:<NSThread: 0x600001996e80>{number = 7, name = (null)} 2020-03-14 23:58:11.874343+0800 ThreadDemo[77370:8259684] thread-1:<NSThread: 0x6000019a55c0>{number = 5, name = (null)} 2020-03-14 23:58:11.874672+0800 ThreadDemo[77370:8259501] dispatch_group_wait後繼續執行任務 2020-03-14 23:58:13.877077+0800 ThreadDemo[77370:8259501] thread-3:<NSThread: 0x6000019fe480>{number = 1, name = main} 2020-03-14 23:58:13.877365+0800 ThreadDemo[77370:8259501] group-end
在這裏須要說明的一點是
dispatch_group_wait
,該方法須要傳入兩個參數,第一個參數是group即調度組,第二個參數是timerout即指定等待的時間。一旦調用dispatch_group_wait
函數,該函數就處理調用的狀態而不返回值,只有當函數的currentThread中止,或到達wait函數指定的等待的時間,或Dispatch Group
中的操做所有執行完畢以前,執行該函數的線程中止。當指定timeout爲DISPATCH_TIME_FOREVER
時就意味着永久等待;當指定timeout爲DISPATCH_TIME_NOW
時就意味不用任何等待便可斷定屬於Dispatch Group
的處理是否所有執行結束。若是dispatch_group_wait
函數返回值不爲0,就意味着雖然通過了指定的時間,但Dispatch Group
中的操做並未所有執行完畢。若是dispatch_group_wait
函數返回值爲0,就意味着Dispatch Group
中的操做所有執行完畢。
下面看dispatch_group_enter
和dispatch_group_leave
捉對實現將隊列添加到調度組的狀況。dispatch_group_enter
標誌着在group的任務數+1,dispatch_group_leave
標誌着在group中的任務數-1,表示已經完成了一個任務。
- (void)groupEnterTest {
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_enter(group);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"thread_1:%@", [NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"thread_2:%@", [NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"thread_3:%@", [NSThread currentThread]);
NSLog(@"group_end");
});
}
複製代碼
打印結果: 2020-03-15 00:07:17.293333+0800 ThreadDemo[77525:8269621] thread_2:<NSThread: 0x600002434ac0>{number = 7, name = (null)} 2020-03-15 00:07:17.293320+0800 ThreadDemo[77525:8269427] thread_1:<NSThread: 0x60000241b9c0>{number = 3, name = (null)} 2020-03-15 00:07:19.294186+0800 ThreadDemo[77525:8269235] thread_3:<NSThread: 0x600002450b80>{number = 1, name = main} 2020-03-15 00:07:19.294485+0800 ThreadDemo[77525:8269235] group_end
須要注意的是
dispatch_group_enter
和dispatch_group_leave
捉對出現的。
GCD中的信號量是指的Dispatch Semaphore
,是持有計數的信號。當信號量小於0時就會一直等待即阻塞所在線程,不然就能夠正常執行。信號量能夠保持線程的同步,將異步執行任務轉換成同步任務執行, 同時保持線程的安全。
Dispatch Semaphore提供了三個方法:
dispatch_semaphore_create
:建立一個 Semaphore 並初始化信號的總量dispatch_semaphore_signal
:發送一個信號,讓信號總量加 1dispatch_semaphore_wait
:可使總信號量減 1,信號總量小於 0 時就會一直等待(阻塞所在線程),不然就能夠正常執行。- (void)semaphoreTest {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block int a = 0;
while (a < 5) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"裏面的a的值:%d-----%@", a, [NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
a++;
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
NSLog(@"外面的a的值:%d", a);
}
複製代碼
打印結果: 2020-03-15 00:44:53.005899+0800 ThreadDemo[78303:8318841] 裏面的a的值:0-----<NSThread: 0x600003c222c0>{number = 3, name = (null)} 2020-03-15 00:44:53.006161+0800 ThreadDemo[78303:8318841] 裏面的a的值:1-----<NSThread: 0x600003c222c0>{number = 3, name = (null)} 2020-03-15 00:44:53.006354+0800 ThreadDemo[78303:8318841] 裏面的a的值:2-----<NSThread: 0x600003c222c0>{number = 3, name = (null)} 2020-03-15 00:44:53.006551+0800 ThreadDemo[78303:8318841] 裏面的a的值:3-----<NSThread: 0x600003c222c0>{number = 3, name = (null)} 2020-03-15 00:44:53.006727+0800 ThreadDemo[78303:8318841] 裏面的a的值:4-----<NSThread: 0x600003c222c0>{number = 3, name = (null)} 2020-03-15 00:44:53.006862+0800 ThreadDemo[78303:8318672] 外面的a的值:5
調度源是協調特殊低級別系統事件處理的基本數據類型。GCD支持諸如定時器調度源、信號調度源、描述符調度源、進程調度源、端口調度源、自定義調度源等。調度源有這一系列的成熟API,在這裏就很少作描述,詳細能夠查閱官方的文檔Dispatch Sources。