GCD相關(guān)知識點
1、 GCD(Grand Centra Dispatch)中隊列分類:串行與并行
在使用GCD的時候,我們會把需要處理的任務(wù)放到Block中,然后將任務(wù)追加到相應(yīng)的隊列里面,這個隊列,叫做Dispatch Queue。然而,存在于兩種Dispatch Queue,一種是要等待上一個執(zhí)行完,再執(zhí)行下一個的Serial Dispatch Queue,這叫做串行隊列;另一種,則是不需要上一個執(zhí)行完,就能執(zhí)行下一個的Concurrent Dispatch Queue,叫做并行隊列,并行隊列能開多少個線程由系統(tǒng)決定。這兩種,均遵循FIFO原則 (并行隊列中,執(zhí)行順序遵循FIFO原則,但是結(jié)果不確定)。
舉一個簡單的例子,在三個任務(wù)中輸出1、2、3,串行隊列輸出是有序的1、2、3,但是并行隊列的先后順序就不一定了。
1.1 系統(tǒng)標準提供的兩個隊列:
// 全局隊列,也是一個并行隊列, 四個優(yōu)先級
dispatch_get_global_queue
// 主隊列,在主線程中運行,因為主線程只有一個,所以這是一個串行隊列
dispatch_get_main_queue
1.2 用戶自己創(chuàng)建的兩個隊列:
// 這是串行隊列
dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL)
// 這是一個并行隊列
dispatch_queue_create("com.demo.concurrentQueue", DISPATCH_QUEUE_CONCURRENT)
//第一個參數(shù)為名字,一定要設(shè)置這個參數(shù),因為發(fā)生崩潰的時候,這個將是一個很重要的指引,這個名字會出現(xiàn)在崩潰信息中,第二個參數(shù)如果要創(chuàng)建serial queue就設(shè)置為NULL,代表空的c指針,如果是另外一種就設(shè)置為DISPATCH_QUEUE_CONCURRENT
//另外要注意的是,雖然我們已經(jīng)步入了ARC時代,但是Dispatch Queue必須由開發(fā)人員來釋放,接著上班的代碼 dispatch_release(myQueueS); 凡是由create創(chuàng)建的對象都要記得手動釋放。但是在iOS6之后無需我們自己管理
In the iOS 6.0 SDK and the Mac OS X 10.8 SDK, every dispatch object is also a part of objective C. So you don't want to worry about the memory, ARC will manage the memory of dispatch_queue
2、GCD中線程分類:同步與異步
串行與并行針對的是隊列,而同步與異步,針對的則是線程。最大的區(qū)別在于,同步線程要阻塞當前線程,必須要等待同步線程中的任務(wù)執(zhí)行完,返回以后,才能繼續(xù)執(zhí)行下一任務(wù);而異步線程則是不用等待。
同步與異步線程的創(chuàng)建:
dispatch_sync(..., ^(block)) // 同步線程,在當前線程中執(zhí)行任務(wù), 不具備開啟線程的能力。
dispatch_async(..., ^(block)) // 異步線程, 在新的線程中執(zhí)行任務(wù), 具備開啟線程的能力, 如果當前隊列是串行的,則不會開啟新的線程。

3、一些方法
3.1 dispatch_set_target_queue
//dispatch_queue_create函數(shù)生成的Dispatch Queue不管是Serial Dispatch Queue還是Concurrent Dispatch Queue,都使用與默認優(yōu)先級Global Dispatch Queue相同執(zhí)行優(yōu)先級的線程,變更執(zhí)行優(yōu)先級使用dispatch_set_target_queue函數(shù)
dispatch_queue_t mySerialQueue = dispatch_queue_create("com.kugou.mySerQueue", NULL);
dispatch_queue_t myGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0);
dispatch_set_target_queue(mySerialQueue, myGlobalQueue);
//指定第一個參數(shù)與第二個參數(shù)優(yōu)先級相同
3.2 dispatch_after
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,3ULL * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(),^{
NSLog(@"123");
})
//注意,dispatch_after函數(shù)并不是在指定時間后執(zhí)行,而只是在指定時間追加處理到dispatch_queue,此源碼在3秒后用dispatch_async函數(shù)追加block到main dispatch queue相同。在嚴格時間要求下這個函數(shù)的使用會出問題。
3.3 dispatch_group
//eg1:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_group_async(group, queue, ^{
sleep(3);
HDLog(@"1");
}); //任務(wù)1
dispatch_group_async(group, queue, ^{
sleep(2);
HDLog(@"2");
}); //任務(wù)2
dispatch_group_async(group, queue, ^{
sleep(1);
HDLog(@"3");
}); //任務(wù)3
dispatch_group_notify(group, dispatch_get_main_queue(),^{
HDLog(@"done");
});
// 3 2 1 done
//任務(wù)1、2、3完成后會通知到block中,但是如果任務(wù)1、2、3是在其他隊列中異步處理比如網(wǎng)絡(luò)請求,則該方法起不到所以接口成功后通知的功能
//eg2:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_group_async(group, queue, ^{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(3);
HDLog(@"1"); //任務(wù)1
});
});
dispatch_group_async(group, queue, ^{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
HDLog(@"2"); //任務(wù)2
});
});
dispatch_group_async(group, queue, ^{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
HDLog(@"3"); //任務(wù)3
});
});
dispatch_group_notify(group, dispatch_get_main_queue(),^{
//dispatch_group_notify 以異步的方式工作, 不阻塞當前線程。當 Dispatch Group 中沒有任何任務(wù)時,它就會執(zhí)行其代碼,那么 completionBlock 便會運行。你還指定了運行 completionBlock 的隊列,此處,主隊列就是你所需要的。
HDLog(@"done");
});
// done 3 2 1
// 如果任務(wù)1、2、3是在其他隊列中異步處理比如網(wǎng)絡(luò)請求,則該方法起不到所以接口成功后通知的功能,由此可見 dispatch_group 也只能在當前的 queue 進行任務(wù)結(jié)束監(jiān)聽。
long result = dispatch_group_wait(group, DISPATCH_TIME_FOREVER)];
// 它會阻塞當前線程, 第一個參數(shù)表示要等待哪個group,第二個參數(shù)是等待的時間,這里也可以設(shè)置一個dispatch_time,如果這個函數(shù)返回值為0,則表示你全部處理完畢,否則代表還有函數(shù)沒有返回。等待的含義是,一旦調(diào)用wait函數(shù),調(diào)用這個函數(shù)的線程就會停止,在經(jīng)過指定的時間或者是group中的全部執(zhí)行都結(jié)束之后,該函數(shù)返回。
// 如果有結(jié)束操作這種需求推薦 dispatch_group_notify 方案。
//eg3:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_group_async(group, queue, ^{
dispatch_group_enter(group); //dispatch_group_enter 手動通知 Dispatch Group 任務(wù)已經(jīng)開始。你必須保證 dispatch_group_enter 和 dispatch_group_leave 成對出現(xiàn),否則你可能會遇到詭異的崩潰問題。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(3);
HDLog(@"1"); //任務(wù)1
dispatch_group_leave(group); //手動通知 Group 它的工作已經(jīng)完成。再次說明,你必須要確保進入 Group 的次數(shù)和離開 Group 的次數(shù)相等。
});
});
dispatch_group_async(group, queue, ^{
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
HDLog(@"2"); //任務(wù)2
dispatch_group_leave(group);
});
});
dispatch_group_async(group, queue, ^{
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
HDLog(@"3"); //任務(wù)3
dispatch_group_leave(group);
});
});
dispatch_group_notify(group, dispatch_get_main_queue(),^{
HDLog(@"done");
});
// 3 2 1 done
// 如果任務(wù)1、2、3是在其他隊列中異步處理比如網(wǎng)絡(luò)請求,則該方法起不到所以接口成功后通知的功能,由此可見 dispatch_group 也只能在當前的 queue 進行任務(wù)結(jié)束監(jiān)聽。
3.4 dispatch_barrier (柵欄)
dispatch_queue_t queueC = dispatch_queue_create("com.kugou.gcd.myQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, blk0_reading);
dispatch_async(queue, blk1_reading);
dispatch_async(queue, blk2_reading);
dispatch_barrier_async(queue, blk3_writing);
dispatch_async(queue, blk4_reading);
dispatch_async(queue, blk5_reading);
dispatch_async(queue, blk6_reading);
// 系統(tǒng)會等0,1,2并發(fā)執(zhí)行完,執(zhí)行blk3,等blk3執(zhí)行完再并發(fā)執(zhí)行4,5,6
dispatch_queue_t queueC = dispatch_queue_create("com.demo.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queueC, ^{
sleep(3);
NSLog(@"1");
});
dispatch_async(queueC, ^{
sleep(2);
NSLog(@"2");
});
dispatch_barrier_sync(queueC, ^{
NSLog(@"3");
});
dispatch_async(queueC, ^{
sleep(1);
NSLog(@"4");
});
// 打印結(jié)果 : 2 1 3 4 dispatch_barrier_sync只能控制當前queueC的順序,但是如果在queueC中又有了其他的異步線程,dispatch_barrier_sync 將無法控制, 比如下面??:
dispatch_queue_t queueC = dispatch_queue_create("com.demo.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queueC, ^{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(3);
NSLog(@"1");
});
});
dispatch_async(queueC, ^{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2);
NSLog(@"2");
});
});
dispatch_barrier_sync(queueC, ^{
NSLog(@"3");
});
dispatch_async(queueC, ^{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
NSLog(@"4");
});
});
// 打印結(jié)果:3 4 2 1
// 在訪問數(shù)據(jù)庫或文件時, 為了提高效率, 讀取操作放在并行隊列中執(zhí)行. 但是寫入操作必須在串行隊列中執(zhí)行(避免資源搶奪問題). 為了避免麻煩, 此時dispatch_barrier_async函數(shù)作用就出來了, 在這函數(shù)里進行寫入操作, 寫入操作會等到所有讀取操作完畢后, 形成一道柵欄, 然后進行寫入操作, 寫入完畢后再把柵欄移除, 同時開放讀取操作.
dispatch_barrier_sync和dispatch_barrier_async:
共同點:
1、等待在它前面插入隊列的任務(wù)先執(zhí)行完;
2、等待他們自己的任務(wù)執(zhí)行完再執(zhí)行后面的任務(wù)。
不同點:
1、dispatch_barrier_sync將自己的任務(wù)插入到隊列的時候,需要等待自己的任務(wù)結(jié)束之后才會繼續(xù)插入被寫在它后面的任務(wù),然后執(zhí)行它們;
2、dispatch_barrier_async將自己的任務(wù)插入到隊列之后,不會等待自己的任務(wù)結(jié)束,它會繼續(xù)把后面的任務(wù)插入到隊列,然后等待自己的任務(wù)結(jié)束后才執(zhí)行后面任務(wù)。
處理多線程時讀寫操作造成線程不安全,使用 dispatch_barrier 時,有如下幾種隊列來加入 dispatch_barrier:
1、dispatch_get_main_queue中加入 dispatch_barrier,明顯不合理,這些文件讀存操作不應(yīng)該在主現(xiàn)場執(zhí)行,并且不應(yīng)該在穿行隊列執(zhí)行,不然將毫無意義;
2、自定義串行隊列:一個很壞的選擇;障礙不會有任何幫助,因為不管怎樣,一個串行隊列一次都只執(zhí)行一個操作;
3、全局并發(fā)隊列:要小心;這可能不是最好的主意,因為其它系統(tǒng)可能在使用隊列而且你不能壟斷它們只為你自己的目的;
4、自定義并發(fā)隊列:這對于原子或臨界區(qū)代碼來說是極佳的選擇。任何你在設(shè)置或?qū)嵗男枰€程安全的事物都是使用障礙的最佳候選。(OK)
一個例子:
PhotoManager.m:
@interface PhotoManager ()
@property (nonatomic,strong,readonly) NSMutableArray *photosArray;
@property (nonatomic, strong) dispatch_queue_t concurrentPhotoQueue; ///< Add this
@end
+ (instancetype)sharedManager {
static PhotoManager *sharedPhotoManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedPhotoManager = [[PhotoManager alloc] init];
sharedPhotoManager->_photosArray = [NSMutableArray array];
sharedPhotoManager->_concurrentPhotoQueue = dispatch_queue_create("com.selander.GooglyPuff.photoQueue",
DISPATCH_QUEUE_CONCURRENT);
});
return sharedPhotoManager;
}
- (NSArray *)photos {
__block NSArray *array; // 1 __block 關(guān)鍵字允許對象在 Block 內(nèi)可變。沒有它,array 在 Block 內(nèi)部就只是只讀的,你的代碼甚至不能通過編譯。
dispatch_sync(self.concurrentPhotoQueue, ^{ // 2 在 concurrentPhotoQueue 上同步調(diào)度來執(zhí)行讀操作。
array = [NSArray arrayWithArray:_photosArray]; // 3 將相片數(shù)組存儲在 array 內(nèi)并返回它。
});
return array;
}
- (void)addPhoto:(Photo *)photo {
if (photo) { // 1 在執(zhí)行下面所有的工作前檢查是否有合法的相片。
dispatch_barrier_async(self.concurrentPhotoQueue, ^{ // 2 添加寫操作到你的自定義隊列。當臨界區(qū)在稍后執(zhí)行時,這將是你隊列中唯一執(zhí)行的條目。
[_photosArray addObject:photo]; // 3 這是添加對象到數(shù)組的實際代碼。由于它是一個障礙 Block ,這個 Block 永遠不會同時和其它 Block 一起在 concurrentPhotoQueue 中執(zhí)行。
dispatch_async(dispatch_get_main_queue(), ^{ // 4 最后你發(fā)送一個通知說明完成了添加圖片。這個通知將在主線程被發(fā)送因為它將會做一些 UI 工作,所以在此為了通知,你異步地調(diào)度另一個任務(wù)到主線程。
[self postContentAddedNotification];
});
});
}
}

3.5 dispatch_apply
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_apply(10, queue, ^(size_t index){
NSLog(@"%zu", index);
});
NSLog(@"done");
// 0 1 2 3 4 5 6 7 8 9 done (其實大部分測試都是按照順序執(zhí)行,但是將10變成100,就有所變動)
dispatch_apply 表現(xiàn)得就像一個 for 循環(huán),但它能并發(fā)地執(zhí)行不同的迭代。這個函數(shù)是同步的,所以和普通的 for 循環(huán)一樣,它只會在所有工作都完成后才會返回。
當在 Block 內(nèi)計算任何給定數(shù)量的工作的最佳迭代數(shù)量時,必須要小心,因為過多的迭代和每個迭代只有少量的工作會導致大量開銷以致它能抵消任何因并發(fā)帶來的收益。而被稱為跨越式(striding)的技術(shù)可以在此幫到你,即通過在每個迭代里多做幾個不同的工作。
那何時才適合用 dispatch_apply 呢?
1、自定義串行隊列:串行隊列會完全抵消 dispatch_apply 的功能;你還不如直接使用普通的 for 循環(huán)。
2、主隊列(串行):與上面一樣,在串行隊列上不適合使用 dispatch_apply 。還是用普通的 for 循環(huán)吧。
3、并發(fā)隊列:對于并發(fā)循環(huán)來說是很好選擇,特別是當你需要追蹤任務(wù)的進度時。(OK)
注意點:
1、你創(chuàng)建并行運行線程而付出的開銷,很可能比直接使用 for 循環(huán)要多。若你要以合適的步長迭代非常大的集合,那才應(yīng)該考慮使用 dispatch_apply。
2、你用于創(chuàng)建應(yīng)用的時間是有限的——除非實在太糟糕否則不要浪費時間去提前優(yōu)化代碼。如果你要優(yōu)化什么,那去優(yōu)化那些明顯值得你付出時間的部分。你可以通過在 Instruments 里分析你的應(yīng)用,找出最長運行時間的方法??纯?如何在 Xcode 中使用 Instruments 可以學到更多相關(guān)知識。
3、通常情況下,優(yōu)化代碼會讓你的代碼更加復雜,不利于你自己和其他開發(fā)者閱讀。請確保添加的復雜性能換來足夠多的好處。
3.6 dispatch_semaphore_t (信號量)
dispatch_semaphore是GCD基于計數(shù)器的一種多線程同步機制,解決因為多線程的特性而引發(fā)數(shù)據(jù)出錯的問題,與他相關(guān)的共有三個函數(shù),分別是
dispatch_semaphore_create,dispatch_semaphore_signal,dispatch_semaphore_wait。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); //創(chuàng)建一個信號量。參數(shù)指定信號量的起始值。這個數(shù)字是你可以訪問的信號量,不需要有人先去增加它的數(shù)量。(注意到增加信號量也被叫做發(fā)射信號量)。譯者注:這里初始化為0,也就是說,有人想使用信號量必然會被阻塞,直到有人增加信號量。這里的傳入的參數(shù)value必須大于或等于0,否則dispatch_semaphore_create會返回NULL
[self block:^{
NSLog(@"1"); //任務(wù)1
dispatch_semaphore_signal(semaphore); //在這里你告訴信號量你不再需要資源了。這就會增加信號量的計數(shù)并告知其他想使用此資源的線程。這個函數(shù)會使傳入的信號量dsema的值加1
}];
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC);
dispatch_semaphore_wait(semaphore, timeout); //這個函數(shù)的作用是這樣的,如果dsema信號量的值大于0,該函數(shù)所處線程就繼續(xù)執(zhí)行下面的語句,并且將信號量的值減1;如果desema的值為0,那么這個函數(shù)就阻塞當前線程等待timeout(注意timeout的類型為dispatch_time_t,不能直接傳入整形或float型數(shù)),如果等待的期間desema的值被dispatch_semaphore_signal函數(shù)加1了,且該函數(shù)(即dispatch_semaphore_wait)所處線程獲得了信號量,那么就繼續(xù)向下執(zhí)行并將信號量減1。如果等待期間沒有獲取到信號量或者信號量的值一直為0,那么等到timeout時,其所處線程自動執(zhí)行其后語句。
測試一:
NSLog(@"111");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"222");
});
NSLog(@"333");
//結(jié)果:
// 打印 "111" 后 程序卡死
// 當前是串行隊列,當執(zhí)行到 dispatch_sync 時,因為是同步操作,所以 dispatch_sync 需要等待 block 執(zhí)行完之后才能向下執(zhí)行,當執(zhí)行 NSLog(@"222"); 的時候會加入到主現(xiàn)場中,但是這個時候主現(xiàn)程正是 dispatch_sync 在執(zhí)行,所以 block 的 NSLog(@"222") 需要等待 dispatch_sync 執(zhí)行完之后才能向下執(zhí)行。這樣就你等我,我等你,造成了死鎖
測試二:
NSLog(@"1"); // 任務(wù)1
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"2"); // 任務(wù)2
});
NSLog(@"3"); // 任務(wù)3
// 打印結(jié)果 1 2 3

測試三:
dispatch_queue_t queue = dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL);
NSLog(@"1"); // 任務(wù)1
dispatch_async(queue, ^{
NSLog(@"2"); // 任務(wù)2
NSLog(@"4"); // 任務(wù)4
});
NSLog(@"5"); // 任務(wù)5
NSLog(@"6"); // 任務(wù)6
// 打印結(jié)果: 1 5 6 2 4
//首先建立了一個串行的自定義隊列,執(zhí)行任務(wù)1后,遇到異步線程,不會等待執(zhí)行任務(wù)2便會往下走,從而執(zhí)行任務(wù)5,網(wǎng)上說的 5 2 順序不確定,但是我用模擬器及真機測試發(fā)現(xiàn)任務(wù)5確實在任務(wù)2之前執(zhí)行。
測試四:
dispatch_queue_t queue = dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL);
NSLog(@"1"); // 任務(wù)1
dispatch_async(queue, ^{
NSLog(@"2"); // 任務(wù)2
dispatch_sync(queue, ^{
NSLog(@"3"); // 任務(wù)3
});
NSLog(@"4"); // 任務(wù)4
});
NSLog(@"5"); // 任務(wù)5
// 打印結(jié)果: 1 5 2 程序卡死
//首先建立了一個串行的自定義隊列,執(zhí)行任務(wù)1后,遇到異步線程dispatch_async,不會等待執(zhí)行任務(wù)2便會往下走,從而執(zhí)行任務(wù)5,網(wǎng)上說的 5 2 順序不確定,但是我用模擬器及真機測試發(fā)現(xiàn)任務(wù)5確實在任務(wù)2之前執(zhí)行。執(zhí)行任務(wù)2之后,遇到了同步線程dispatch_sync,需要等待任務(wù)3執(zhí)行完才能繼續(xù)往下走,但此時任務(wù)3也串行隊列中,而此時的串行隊列正被 dispatch_sync 占用著,這樣就你等我,我等你,造成了死鎖
測試五:
NSLog(@"1"); // 任務(wù)1
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2"); // 任務(wù)2
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"3"); // 任務(wù)3
});
NSLog(@"4"); // 任務(wù)4
});
NSLog(@"5"); // 任務(wù)5
// 打印結(jié)果: 1 5 2 3 4

測試六:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1"); // 任務(wù)1
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2"); // 任務(wù)2
});
NSLog(@"3"); // 任務(wù)3
});
NSLog(@"4"); // 任務(wù)4
while (1) {
}
NSLog(@"5"); // 任務(wù)5
// 打印結(jié)果: 4 1 程序卡住
// Main Queue:【異步線程、任務(wù)4、死循環(huán)、任務(wù)5、任務(wù)2】
// Global Queue異步線程中的任務(wù)有:【任務(wù)1、同步線程、任務(wù)3】
// 因為 dispatch_async 在 并行隊列中,并且異步執(zhí)行,所以任務(wù)4無需等待立刻執(zhí)行,然后程序進入死循環(huán),Main Queue阻塞。但是加入到Global Queue的異步線程不受影響,繼續(xù)執(zhí)行任務(wù)1后面的同步線程。這個時候執(zhí)行Main Queue隊列中的同步線程,任務(wù)3需要在任務(wù)2執(zhí)行完才能執(zhí)行,但是此時任務(wù)2在主線程中無法執(zhí)行,所以卡住。

4、一些問題:
4.1 并發(fā)和并行
并行(parallelism)
這個概念很好理解。所謂并行,就是同時執(zhí)行的意思,無需過度解讀。判斷程序是否處于并行的狀態(tài),就看同一時刻是否有超過一個“工作單位”在運行就好了。所以,單線程永遠無法達到并行狀態(tài)。
要達到并行狀態(tài),最簡單的就是利用多線程和多進程。
并發(fā)(Concurrent)
使多個操作可以在重疊的時間段內(nèi)進行(two tasks can start, run, and complete in overlapping time periods)。單核單線程能支持并發(fā)。
例子說明:
你吃飯吃到一半,電話來了,你一直到吃完了以后才去接,這就說明你不支持并發(fā)也不支持并行。你吃飯吃到一半,電話來了,你停了下來接了電話,接完后繼續(xù)吃飯,這說明你支持并發(fā)。你吃飯吃到一半,電話來了,你一邊打電話一邊吃飯,這說明你支持并行。并發(fā)的關(guān)鍵是你有處理多個任務(wù)的能力,不一定要同時。并行的關(guān)鍵是你有同時處理多個任務(wù)的能力。
