GCD简介
1.隊列
-
串行隊列,串行隊列將任務(wù)以先進(jìn)先出(FIFO)的順序來執(zhí)行,所以串行隊列經(jīng)常用來做訪問某些特定資源的同步處理。你可以也根據(jù)需要創(chuàng)建多個隊列,而這些隊列相對其他隊列都是并發(fā)執(zhí)行的。換句話說,如果你創(chuàng)建了4個串行隊列,每一個隊列在同一時間都只執(zhí)行一個任務(wù),對這四個任務(wù)來說,他們是相互獨(dú)立且并發(fā)執(zhí)行的。如果需要創(chuàng)建串行隊列,一般用dispatch_queue_create這個方法來實(shí)現(xiàn),并指定隊列類型DISPATCH_QUEUE_SERIAL。
-
并發(fā)隊列,并發(fā)隊列雖然是能同時執(zhí)行多個任務(wù),但這些任務(wù)仍然是按照先到先執(zhí)行(FIFO)的順序來執(zhí)行的。并發(fā)隊列會基于系統(tǒng)負(fù)載來合適地選擇并發(fā)執(zhí)行這些任務(wù)。并發(fā)隊列一般指的就是全局隊列(Global queue),進(jìn)程中存在四個全局隊列:高、中(默認(rèn))、低、后臺四個優(yōu)先級隊列,可以調(diào)用dispatch_get_global_queue函數(shù)傳入優(yōu)先級來訪問隊列。當(dāng)然我們也可以用dispatch_queue_create,并指定隊列類型DISPATCH_QUEUE_CONCURRENT,來自己創(chuàng)建一個并發(fā)隊列。
-
主隊列,與主線程功能相同。實(shí)際上,提交至main queue的任務(wù)會在主線程中執(zhí)行。main queue可以調(diào)用dispatch_get_main_queue()來獲得。因為main queue是與主線程相關(guān)的,所以這是一個串行隊列。和其它串行隊列一樣,這個隊列中的任務(wù)一次只能執(zhí)行一個。它能保證所有的任務(wù)都在主線程執(zhí)行,而主線程是唯一可用于更新 UI 的線程。
2.任務(wù)
-
同步任務(wù),使用dispatch_sync將任務(wù)加入隊列。將同步任務(wù)加入串行隊列,會順序執(zhí)行,一般不這樣做并且在一個任務(wù)未結(jié)束時調(diào)起其它同步任務(wù)會死鎖。將同步任務(wù)加入并行隊列,會順序執(zhí)行,但是也沒什么意義。
-
異步任務(wù),使用dispatch_async將任務(wù)加入隊列。將異步任務(wù)加入串行隊列,會順序執(zhí)行,并且不會出現(xiàn)死鎖問題。將異步任務(wù)加入并行隊列,會并行執(zhí)行多個任務(wù),這也是我們最常用的一種方式。
3.GCD常見的用法和應(yīng)用場景
3.1 dispatch_async(常見的應(yīng)用場景是異步處理耗時的操作,然后耗時操作處理完畢后,使用主線程更新UI)
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(globalQueue, ^{// 一個異步的任務(wù),例如網(wǎng)絡(luò)請求,耗時的文件操作等等...dispatch_async(dispatch_get_main_queue(), ^{// UI刷新...}); }); 復(fù)制代碼3.2 dispatch_after (常用的應(yīng)用場景是延時調(diào)用)
dispatch_queue_t queue= dispatch_get_main_queue(); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), queue, ^{// 在queue里面延遲執(zhí)行的一段代碼... }); 復(fù)制代碼3.3 dispatch_once (常用于單例的創(chuàng)建,只創(chuàng)建一次)
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{// 只執(zhí)行一次的任務(wù)... }); 復(fù)制代碼3.4 dispatch_group (GCD組,把一組任務(wù)提交到隊列中,多個請求完畢后才處理事情,如多個網(wǎng)絡(luò)請求完畢會,才去更新UI)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, queue, ^{// 異步任務(wù)1 });dispatch_group_async(group, queue, ^{// 異步任務(wù)2 });// 等待group中多個異步任務(wù)執(zhí)行完畢,做一些事情,介紹兩種方式// 方式1(不好,會卡住當(dāng)前線程) dispatch_group_wait(group, DISPATCH_TIME_FOREVER); ...// 方式2(比較好) dispatch_group_notify(group, mainQueue, ^{// 任務(wù)完成后,在主隊列中做一些操作... }); 復(fù)制代碼3.5 dispatch_barrier_async(和dispatch_group類似,dispatch_barrier也是異步任務(wù)間的一種同步方式,可以在比如文件的讀寫操作時使用,保證讀操作的準(zhǔn)確性。另外,有一點(diǎn)需要注意,dispatch_barrier_sync和dispatch_barrier_async只在自己創(chuàng)建的并發(fā)隊列上有效,在全局(Global)并發(fā)隊列、串行隊列上,效果跟dispatch_(a)sync效果一樣)
// dispatch_barrier_async的作用可以用一個詞概括--承上啟下,它保證此前的任務(wù)都先于自己執(zhí)行,此后的任務(wù)也遲于自己執(zhí)行。本例中,任務(wù)4會在任務(wù)1、2、3都執(zhí)行完之后執(zhí)行,而任務(wù)5、6會等待任務(wù)4執(zhí)行完后執(zhí)行。dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{// 任務(wù)1... }); dispatch_async(queue, ^{// 任務(wù)2... }); dispatch_async(queue, ^{// 任務(wù)3... }); dispatch_barrier_async(queue, ^{// 任務(wù)4... }); dispatch_async(queue, ^{// 任務(wù)5... }); dispatch_async(queue, ^{// 任務(wù)6... }); 復(fù)制代碼3.6 dispatch_apply(dispatch_apply有什么用呢,因為dispatch_apply并行的運(yùn)行機(jī)制,效率一般快于for循環(huán)的類串行機(jī)制(在for一次循環(huán)中的處理任務(wù)很多時差距比較大)。比如這可以用來拉取網(wǎng)絡(luò)數(shù)據(jù)后提前算出各個控件的大小,防止繪制時計算,提高表單滑動流暢性,如果用for循環(huán),耗時較多,并且每個表單的數(shù)據(jù)沒有依賴關(guān)系,所以用dispatch_apply比較好)
// for循環(huán)做一些事情,輸出0123456789 for (int i = 0; i < 10; i ++) {NSLog(@"%d", i); }// dispatch_apply替換(當(dāng)且僅當(dāng)處理順序?qū)μ幚斫Y(jié)果無影響環(huán)境),輸出順序不定,比如1098673452 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); /*! dispatch_apply函數(shù)說明 * * @brief dispatch_apply函數(shù)是dispatch_sync函數(shù)和Dispatch Group的關(guān)聯(lián)API * 該函數(shù)按指定的次數(shù)將指定的Block追加到指定的Dispatch Queue中,并等到全部的處理執(zhí)行結(jié)束 * * @param 10 指定重復(fù)次數(shù) 指定10次 * @param queue 追加對象的Dispatch Queue * @param index 帶有參數(shù)的Block, index的作用是為了按執(zhí)行的順序區(qū)分各個Block * */ dispatch_apply(10, queue, ^(size_t index) {NSLog(@"%zu", index); }); 復(fù)制代碼3.7 dispatch_suspend和dispatch_resume(隊列的暫停和恢復(fù),已添加到隊列中沒有執(zhí)行的任務(wù)不會執(zhí)行,直至等到線程恢復(fù)才會繼續(xù)執(zhí)行)
dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_suspend(queue); //暫停隊列queue dispatch_resume(queue); //恢復(fù)隊列queue 復(fù)制代碼3.8 dispatch_semaphore_signal
dispatch_semaphore 信號量基于計數(shù)器的一種多線程同步機(jī)制。在多個線程訪問共有資源時候,會因為多線程的特性而引發(fā)數(shù)據(jù)出錯的問題。
// dispatch_semaphore_signal有兩類用法:a、解決同步問題;b、解決有限資源訪問(資源為1,即互斥)問題。 // dispatch_semaphore_wait,若semaphore計數(shù)為0則等待,大于0則使其減1。 // dispatch_semaphore_signal使semaphore計數(shù)加1。// a、同步問題:輸出肯定為1、2、3。 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_semaphore_t semaphore1 = dispatch_semaphore_create(1); dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(0); dispatch_semaphore_t semaphore3 = dispatch_semaphore_create(0);dispatch_async(queue, ^{// 任務(wù)1dispatch_semaphore_wait(semaphore1, DISPATCH_TIME_FOREVER);NSLog(@"1\n");dispatch_semaphore_signal(semaphore2);dispatch_semaphore_signal(semaphore1); });dispatch_async(queue, ^{// 任務(wù)2dispatch_semaphore_wait(semaphore2, DISPATCH_TIME_FOREVER);NSLog(@"2\n");dispatch_semaphore_signal(semaphore3);dispatch_semaphore_signal(semaphore2); });dispatch_async(queue, ^{// 任務(wù)3dispatch_semaphore_wait(semaphore3, DISPATCH_TIME_FOREVER);NSLog(@"3\n");dispatch_semaphore_signal(semaphore3); });// b、有限資源訪問問題:for循環(huán)看似能創(chuàng)建100個異步任務(wù),實(shí)質(zhì)由于信號限制,最多創(chuàng)建10個異步任務(wù)。 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_semaphore_t semaphore = dispatch_semaphore_create(10); for (int i = 0; i < 100; i ++) {dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);dispatch_async(queue, ^{// 任務(wù)...dispatch_semaphore_signal(semaphore);}); } 復(fù)制代碼dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 如果semaphore計數(shù)大于等于1.計數(shù)-1,返回,程序繼續(xù)運(yùn)行。 如果計數(shù)為0,則等待。 這里設(shè)置的等待時間是一直等待。
dispatch_semaphore_signal(semaphore); 計數(shù)+1. 在這兩句代碼中間的執(zhí)行代碼,每次只會允許一個線程進(jìn)入,這樣就有效的保證了在多線程環(huán)境下,只能有一個線程進(jìn)入。
3.9 dispatch_set_context、dispatch_get_context和dispatch_set_finalizer_f(dispatch_set_context可以為隊列添加上下文數(shù)據(jù),但是因為GCD是C語言接口形式的,所以其context參數(shù)類型是“void *”。需使用上述abc三種方式創(chuàng)建context,并且一般結(jié)合dispatch_set_finalizer_f使用,回收context內(nèi)存)
// dispatch_set_context、dispatch_get_context是為了向隊列中傳遞上下文context服務(wù)的。 // dispatch_set_finalizer_f相當(dāng)于dispatch_object_t的析構(gòu)函數(shù)。 // 因為context的數(shù)據(jù)不是foundation對象,所以arc不會自動回收,一般在dispatch_set_finalizer_f中手動回收,所以一般講上述三個方法綁定使用。- (void)test {// 幾種創(chuàng)建context的方式// a、用C語言的malloc創(chuàng)建context數(shù)據(jù)。// b、用C++的new創(chuàng)建類對象。// c、用Objective-C的對象,但是要用__bridge等關(guān)鍵字轉(zhuǎn)為Core Foundation對象。dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);if (queue) {// "123"即為傳入的contextdispatch_set_context(queue, "123");dispatch_set_finalizer_f(queue, &xigou);}dispatch_async(queue, ^{char *string = dispatch_get_context(queue);NSLog(@"%s", string);}); }// 該函數(shù)會在dispatch_object_t銷毀時調(diào)用。 void xigou(void *context) {// 釋放context的內(nèi)存(對應(yīng)上述abc)// a、CFRelease(context);// b、free(context);// c、delete context; } 復(fù)制代碼4. 常見的死鎖
4.1 dispatch_sync
// 假設(shè)這段代碼執(zhí)行于主隊列 dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL); dispatch_queue_t mainQueue = dispatch_get_main_queue();// 在主隊列添加同步任務(wù) dispatch_sync(mainQueue, ^{// 任務(wù)... });// 在串行隊列添加同步任務(wù) dispatch_sync(serialQueue, ^{// 任務(wù)...dispatch_sync(serialQueue, ^{// 任務(wù)...}); }; 復(fù)制代碼4.2 dispatch_apply
// 因為dispatch_apply會卡住當(dāng)前線程,內(nèi)部的dispatch_apply會等待外部,外部的等待內(nèi)部,所以死鎖。 dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT); dispatch_apply(10, queue, ^(size_t) {// 任務(wù)...dispatch_apply(10, queue, ^(size_t) {// 任務(wù)...}); }); 復(fù)制代碼4.3 dispatch_barrier
dispatch_barrier_sync在串行隊列和全局并行隊列里面和dispatch_sync同樣的效果,所以需考慮同dispatch_sync一樣的死鎖問題。
總結(jié)
- 上一篇: SE《八方旅人 2》正式发售,登陆任天堂
- 下一篇: 美股周五:三大股指均跌超1%,苹果跌近2