GCD相關(guān)總結(jié)

關(guān)鍵詞:異步執(zhí)行任務(wù)的技術(shù)、將應(yīng)用程序的線程管理用的代碼在系統(tǒng)級中實(shí)現(xiàn)、高效率。

舊的 API 實(shí)現(xiàn)
- (void)demoPerformSelector{
    [self performSelectorInBackground:@selector(doWork) withObject:nil];
}

- (void)doWork{
    NSLog(@"doWork........");
    [self performSelectorOnMainThread:@selector(doneWork) withObject:nil waitUntilDone:NO];
}

- (void)doneWork{
    NSLog(@"doneWork!");
}

使用 GCD 實(shí)現(xiàn)
dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"doWork........");
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"doneWork!");
        });
    });

1.Dispatch Queue 調(diào)度隊(duì)列

Dispatch Queue 先進(jìn)先出的屬性(FIFO) 執(zhí)行處理。有兩種 Dispatch Queue,一種是等待現(xiàn)在執(zhí)行中處理的 Serial Dispatch Queue(串行調(diào)度隊(duì)列),另一種是不等待現(xiàn)在執(zhí)行中處理的 Concurrent Dispatch Queue(并行調(diào)度隊(duì)列)。
通過以下源碼,比較這兩種 Dispatch Queue:

dispatch_async(queue,block0);
dispatch_async(queue,block1);
dispatch_async(queue,block2);
dispatch_async(queue,block3);
dispatch_async(queue,block4);
dispatch_async(queue,block5);

當(dāng) queue 為 Serial Dispatch Queue 時(shí),因?yàn)橐鹊浆F(xiàn)在執(zhí)行中處理結(jié)束,所以首先執(zhí)行 block0,block0執(zhí)行結(jié)束后,執(zhí)行 block1,如此重復(fù),同時(shí)執(zhí)行的處理數(shù)只能有1個(gè)。即執(zhí)行該源代碼后,一定按照以下順序進(jìn)行處理。(block0,block1,block2,block3,block4,block5)。
當(dāng) queue 為 Concurrent Dispatch Queue 時(shí),因?yàn)椴挥玫却F(xiàn)在執(zhí)行中的處理結(jié)束,所以首先執(zhí)行 block0,不管 block0 的執(zhí)行是否結(jié)束,都開始執(zhí)行后面的 block1,不管 block1執(zhí)行是否結(jié)束了,都開始執(zhí)行后面的 block2,如此重復(fù)循環(huán)。
這樣雖然不用等待處理結(jié)束,可以并行執(zhí)行多個(gè)處理,但并行執(zhí)行處理的數(shù)量取決于當(dāng)前系統(tǒng)的狀態(tài)。即 iOS 或 OS X 基于 Dispatch Queue 中的處理數(shù)、CPU核數(shù)以及 CPU 負(fù)荷等當(dāng)前系統(tǒng)的狀態(tài)來決定 Concurrent Dispatch Queue 中并行執(zhí)行的處理數(shù)。所謂的“并行執(zhí)行”,就是使用多個(gè)線程同時(shí)執(zhí)行多個(gè)處理。

2.獲取 Dispatch Queue

使用 dispatch_queue_create 創(chuàng)建 Dispatch Queue

//創(chuàng)建 Serial Dispatch Queue
dispatch_queue_t mySerialDicpatchQueue = dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", NULL);
//創(chuàng)建 Concurrent Dispatch Queue
dispatch_queue_t myConcurrentQueue = dispatch_queue_create("com.exmaple.gcd.MuConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);

需要注意的是 dispatch_queue_create函數(shù)可以創(chuàng)建任意多個(gè) Dispatch Queue,當(dāng)生成多個(gè) Serial Dispatch Queue 時(shí),雖然一個(gè) Seria Dispatch Queue 中同時(shí)只能執(zhí)行一個(gè)追加處理,但如果將處理分別追加到多個(gè) Serial Dispatch Queue 中,各個(gè) Serial Dispatch Queue 會(huì)分別執(zhí)行,即同時(shí)執(zhí)行多個(gè)處理。一旦生成 Serial Dispatch Queue 并追加處理,系統(tǒng)對于一個(gè) Serial Dispatch Queue 就只生成并使用一個(gè)線程。如果生成 N 個(gè) Serial Dispatch Queue,那么就生成 N 個(gè)線程。

Main Dispatch Queue 和 Global Dispatch Queue

Main Dispatch Queue,主線程隊(duì)列,是一個(gè) Serial Dispatch Queue

dispatch_queue_t mainQueue = dispatch_get_main_queue();

Global Dispatch Queue,全局隊(duì)列,是 Concurrent Dispatch Queue,有四種優(yōu)先級High Priority、Default Priority、Low Priority、Background Priority

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

3. dispatch_set_target_queue()設(shè)置參考隊(duì)列

dispatch_set_target_queue(dispatch_object_t object,
        dispatch_queue_t _Nullable queue);

該函數(shù)有兩種用法,第一種是設(shè)置 Dispatch Queue 的優(yōu)先級

第一個(gè)參數(shù)填需要更改優(yōu)先級的 Dispatch Queue,第二個(gè)參數(shù)填要與要指定的優(yōu)先級相同優(yōu)先級的 Global Dispatch Queue。

dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.gcd.mySerialDispatchQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t globalQueueLowPriority = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

dispatch_set_target_queue(mySerialDispatchQueue, globalQueueLowPriority);

第二種用法可以用來線程同步
當(dāng)我們想讓不同隊(duì)列中的任務(wù)同步的執(zhí)行時(shí),我們可以創(chuàng)建一個(gè)串行隊(duì)列,然后將這些隊(duì)列的 target 指向新創(chuàng)建的隊(duì)列

    dispatch_queue_t serailQueue1 = dispatch_queue_create("com.gcd.serialQueue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serailQueue2 = dispatch_queue_create("com.gcd.serialQueue2", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t concurrentQueue1 = dispatch_queue_create("com.gcd.concurrentQueue1", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t concurrentQueue2 = dispatch_queue_create("com.gcd.concurrentQueue2", DISPATCH_QUEUE_CONCURRENT);
    
    //創(chuàng)建目標(biāo)隊(duì)列(參考隊(duì)列)
    dispatch_queue_t targetQueue = dispatch_queue_create("com.gcd.targetQueue", DISPATCH_QUEUE_SERIAL);
    //設(shè)置參考
    dispatch_set_target_queue(serailQueue1, targetQueue);
    dispatch_set_target_queue(serailQueue2, targetQueue);
    dispatch_set_target_queue(concurrentQueue1, targetQueue);
    dispatch_set_target_queue(concurrentQueue2, targetQueue);

    NSLog(@"******start******");
    dispatch_async(serailQueue1, ^{
        NSLog(@"current Thread:%@ task1",[NSThread currentThread]);
        sleep(3);
        NSLog(@"task1 end");
    });
    dispatch_async(serailQueue2, ^{
        NSLog(@"current Thread:%@ task2",[NSThread currentThread]);
        sleep(2);
        NSLog(@"task2 end");
    });
    dispatch_async(concurrentQueue1, ^{
        NSLog(@"current Thread:%@ task3",[NSThread currentThread]);
        sleep(1);
        NSLog(@"task3 end");
    });
    dispatch_async(concurrentQueue2, ^{
        NSLog(@"current Thread:%@ task4",[NSThread currentThread]);
        NSLog(@"task4 end");
    });
    NSLog(@"******end******");

輸出結(jié)果:

******start******
******end******
current Thread:<NSThread: 0x600000468580>{number = 5, name = (null)} task1 
task1 end
current Thread:<NSThread: 0x600000468580>{number = 5, name = (null)} task2
task2 end
current Thread:<NSThread: 0x600000468580>{number = 5, name = (null)} task3
task3 end
current Thread:<NSThread: 0x600000468580>{number = 5, name = (null)} task4
task4 end

通過dispatch_set_target_queue()函數(shù)以及參考隊(duì)列targetQueue,使得串行隊(duì)列serailQueue1,serailQueue2與并行隊(duì)列concurrentQueue1,concurrentQueue2同步。

4. dispatch_after()延時(shí)執(zhí)行(準(zhǔn)確的說是追加任務(wù))

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
    NSLog(@"**********************");
});

注意:
dispatch_after()函數(shù)并不是在指定時(shí)間后執(zhí)行任務(wù),而是在指定時(shí)間追加任務(wù)到 Dispatch Queue 中。
示例中與3秒后用 dispatch_async()函數(shù)追加 block 中的任務(wù)到 Main Dispatch Queue 相同。
Main Dispatch Queue 在主線程的 RunLoop 中執(zhí)行,假設(shè)每隔 1/60秒執(zhí)行一次的 RunLoop,block 最快在3s 后執(zhí)行,最慢在 3+1/60 秒后執(zhí)行,并且在 Main Dispatch Queue 中有大量追加的任務(wù)或者主線程本身處理有延遲時(shí),時(shí)間會(huì)更長。
dispatch_time_t 表示的是一個(gè)時(shí)刻,可以由 dispatch_time()函數(shù)或者 dispatch_walltime()函數(shù)獲得
dispatch_time()函數(shù) 能夠獲取從第一個(gè)參數(shù)指定的時(shí)間開始,到第二個(gè)參數(shù)指定的納秒(毫微秒)后的時(shí)間 常用于計(jì)算相對時(shí)間
dispatch_walltime()函數(shù)由 POSIX 中使用的 struct timespec 類型的時(shí)間得到 dispatch_time_t 類型的值,常用計(jì)算絕對時(shí)間

 //ull 數(shù)值字面量(unsigned long long) DISPATCH_TIME_NOW 表示現(xiàn)在的時(shí)間
 //獲取從現(xiàn)在開始1s 后的時(shí)間
 dispatch_time_t time1 = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
 //獲取從現(xiàn)在開始100毫秒后的時(shí)間
 dispatch_time_t time2 = dispatch_time(DISPATCH_TIME_NOW, 150ull * NSEC_PER_MSEC);
    // 通過 NSDate 對象獲取 dispatch_time_t 類型值
    dispatch_time_t getDispatchTimeByDate(NSDate *date){
    NSTimeInterval interval = [date timeIntervalSince1970];
    double second;
    double subsecond = modf(interval, &second);
    struct timespec time;
    time.tv_sec = second;
    time.tv_nsec = subsecond * NSEC_PER_SEC;
    dispatch_time_t milestone = dispatch_walltime(&time, 0);
    return milestone;
}

5. Dispatch Group

Dispatch Group可以用于在追加到 Dispatch Queue 中的多個(gè)任務(wù)全部結(jié)束后想執(zhí)行的結(jié)束任務(wù)的操作。

  • dispatch_group_create()創(chuàng)建 Dispatch Group
  • dispatch_group_async()追加任務(wù)到指定的 Dispatch Queue 中,且指定任務(wù)屬于指定的 Dispatch Group
  • dispatch_group_notify()所有任務(wù)執(zhí)行完畢后再追加執(zhí)行的任務(wù)
  • dispatch_group_wati() 在指定的等待時(shí)間前(超時(shí)時(shí)間),等待 group 中全部任務(wù)處理結(jié)束,會(huì)卡住當(dāng)前線程
    dispatch_queue_t serialQueue = dispatch_queue_create("com.gcd.serialQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t conCurrentQueue = dispatch_queue_create("com.gcd.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    
    NSLog(@"******start******");
    dispatch_group_async(group, serialQueue, ^{
        NSLog(@"current Thread:%@ task1",[NSThread currentThread]);
        sleep(3);
        NSLog(@"task1 end");
    });
    dispatch_group_async(group, conCurrentQueue, ^{
        NSLog(@"current Thread:%@ task2",[NSThread currentThread]);
        sleep(2);
        NSLog(@"task2 end");
    });
    dispatch_group_async(group, serialQueue, ^{
        NSLog(@"current Thread:%@ task3",[NSThread currentThread]);
        sleep(1);
        NSLog(@"task3 end");
    });
    dispatch_group_async(group, conCurrentQueue, ^{
        NSLog(@"current Thread:%@ task4",[NSThread currentThread]);
        NSLog(@"task4 end");
    });
    
    //第二個(gè)參數(shù)填超時(shí)時(shí)間 DISPATCH_TIME_FOREVER 表示永遠(yuǎn)等待
//    long result = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    // 設(shè)置2秒的超時(shí)時(shí)間
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2ull * NSEC_PER_SEC);
    long result = dispatch_group_wait(group, time);
    if (result == 0) {
        NSLog(@"Group 中所有任務(wù)執(zhí)行完畢");
    }else{
        
        NSLog(@"Group 中任有任務(wù)執(zhí)行中");
    }
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"current Thread:%@ All END",[NSThread currentThread]);
    });
    NSLog(@"******end******");

6. dispatch_barrier_async

dispatch_barrier_async函數(shù)可以理解為一種分割任務(wù)的柵欄,通過dispatch_barrier_async追加的任務(wù)同時(shí)只執(zhí)行一個(gè)

    dispatch_async(conCurrentQueue, read_block1);
    dispatch_async(conCurrentQueue, read_block2);
    dispatch_async(conCurrentQueue, read_block3);
    
    dispatch_barrier_async(conCurrentQueue, write_block4);

    dispatch_async(conCurrentQueue, read_block5);
    dispatch_async(conCurrentQueue, read_block6);
    dispatch_async(conCurrentQueue, read_block7);

示例中 block1,block2,block3 并行執(zhí)行,都執(zhí)行完畢后會(huì)執(zhí)行 write_block4,然后block5,block6,block7再并行執(zhí)行。
使用 Concurrent Dispatch Queue 配合 dispatch_barrier_async 函數(shù)可以實(shí)現(xiàn)高效的數(shù)據(jù)庫訪問和文件訪問。

7. dispatch_sync

dispatch_sync同步追加任務(wù)到隊(duì)列中,不能開辟線程,且只有在追加的任務(wù)完成后才返回

dispatch_sync 函數(shù)引起死鎖問題

產(chǎn)生死鎖的條件是在串行隊(duì)列所在的線程中,使用 dispatch_sync 函數(shù)追加任務(wù)到該串行隊(duì)列中。

示例一

 //在主線程中調(diào)用以下代碼會(huì)產(chǎn)生死鎖
 dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"死鎖~~");
            NSLog(@"current thread:%@",[NSThread currentThread]);
        });

示例二

 dispatch_queue_t serialDispatchQueue = dispatch_queue_create("com.gcd.serialDispatchQueue", NULL);
    dispatch_async(serialDispatchQueue, ^{
        NSLog(@"current thread:%@",[NSThread currentThread]);
        dispatch_sync(serialDispatchQueue, ^{
            NSLog(@"死鎖");
        });
    });

示例一:由于主線程所在的隊(duì)列 Main Dispatch Queue 為一個(gè)串行隊(duì)列,所以在主線程中使用dispatch_sync函數(shù)同步追加任務(wù)到 Main Dispatch Queue 中會(huì)產(chǎn)生死鎖。
示例二:創(chuàng)建了串行隊(duì)列 serialDispatchQueue,使用dispatch_async異步追加任務(wù)到該隊(duì)列,此時(shí)該隊(duì)列中的任務(wù)都是在該隊(duì)列的線程上執(zhí)行,此時(shí)使用dispatch_sync函數(shù)再同步追加任務(wù)到該隊(duì)列中,由于是在串行隊(duì)列所在的線程中同步追加任務(wù),所以產(chǎn)生了死鎖。

dispatch_sync 函數(shù)引起死鎖的原因

  • 調(diào)用dispatch_sync函數(shù)會(huì)立即阻塞調(diào)用時(shí)該函數(shù)所在的線程,等待dispatch_sync函數(shù)返回
  • 由于追加任務(wù)的隊(duì)列為串行隊(duì)列所以,采用 FIFO 的順序執(zhí)行任務(wù),很顯然我們追加的任務(wù)位于隊(duì)列后面,現(xiàn)在不會(huì)立即執(zhí)行
  • 如果任務(wù)不執(zhí)行完,dispatch_sync函數(shù)不會(huì)返回,所以線程會(huì)一直被阻塞

8. dispatch_apply

dispatch_apply函數(shù)會(huì)按指定的次數(shù)將任務(wù) block 追加到指定的 Dispatch Queue 中,并等待全部處理執(zhí)行結(jié)束。

   dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create("com.gcd.concurrentDispatchQueue", DISPATCH_QUEUE_CONCURRENT);
   dispatch_apply(5, concurrentDispatchQueue, ^(size_t index) {
        NSLog(@"%zu current thread:%@",index,[NSThread currentThread]);
    });
    NSLog(@"******end******");

執(zhí)行結(jié)果:0,2,1,3,4,5 end
因?yàn)槭窃?Concurrent Dispatch Queue 中執(zhí)行任務(wù)的,所以幾個(gè)任務(wù)是并行執(zhí)行。

注意: dispatch_apply函數(shù)會(huì)阻塞當(dāng)前線程,等待任務(wù)全部執(zhí)行完畢再返回,所以在主線程中調(diào)用追加任務(wù)到 Main Dispatch Queue 會(huì)造成死鎖。

9. dispatch_suspenddispatch_resume函數(shù)

dispatch_suspend函數(shù)用于掛起指定的 Dispatch Queue
dispatch_resume函數(shù)用于恢復(fù)指定的 Dispatch Queue
這些函數(shù)對已經(jīng)開始執(zhí)行的任務(wù)沒有影響,掛起后,追加到 Dispatch Queue 中,但尚未執(zhí)行的任務(wù)在此之后會(huì)暫停執(zhí)行(任務(wù)仍然可以繼續(xù)追加,但新追加的也會(huì)暫停執(zhí)行),而恢復(fù)則使得這些任務(wù)繼續(xù)執(zhí)行。

10. Dispatch Semaphore 信號量

Dispatch Semaphore 信號量在 GCD 常被用于進(jìn)行同步和控制并發(fā)。
信號量是一個(gè)整形值并且具有一個(gè)初始計(jì)數(shù)值,并且支持兩個(gè)操作:信號通知和等待。當(dāng)一個(gè)信號量被信號通知,其計(jì)數(shù)會(huì)被加1。當(dāng)在一個(gè)線程上設(shè)置一個(gè)信號量等待時(shí),線程會(huì)被阻塞至超時(shí)時(shí)間(如果有必要的話可以設(shè)置為一直阻塞),只有當(dāng)計(jì)數(shù)器大于零,計(jì)數(shù)才會(huì)被減1并且該線程恢復(fù)。
信號量可以被用來作為線程鎖,也可以用來控制并發(fā)線程數(shù)。

       //如果設(shè)置為10的話,并發(fā)線程最多為10個(gè)
//    dispatch_semaphore_t sema = dispatch_semaphore_create(10);
    //如果設(shè)置為1的話,并發(fā)線程為1個(gè),可以保證數(shù)據(jù)安全
    dispatch_semaphore_t sema = dispatch_semaphore_create(1);
    dispatch_queue_t gloabQueue = dispatch_get_global_queue(0, 0);
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:100];
    for (int i = 0; i < 100; i++) {
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
        dispatch_async(gloabQueue, ^{
            NSLog(@"thread%@ add %d",[NSThread currentThread] ,i);
            [array addObject:@(i)];
            dispatch_semaphore_signal(sema);
        });
    }

11. dispatch_once

dispatch_once函數(shù)是保證在應(yīng)用程序執(zhí)行中執(zhí)行一次指定處理的 API。
使用dispatch_once函數(shù)生成單利,即使在多線程情況下執(zhí)行,也可保證百分百安全。

    static dispatch_once_t pred;
    dispatch_once(&pred, ^{
        NSLog(@"只會(huì)執(zhí)行一次");
    });
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容