GCD用法簡(jiǎn)介

GCD用法

GCD

Dispatch Queue介紹

蘋果官方對(duì)GCD的說明:開發(fā)者要做的只是定義想執(zhí)行的任務(wù)并追加到適當(dāng)?shù)腄ispatch Queue中

dispatch_async(queue, ^{
//要執(zhí)行的任務(wù)
});

多線程編程定義

1個(gè)CPU執(zhí)行的CPU命令列為一條無分叉的路徑,即為”線程“。目前,基本上一個(gè)CPU核一次只能執(zhí)行一個(gè)CPU命令,那么怎樣才能在多條路徑中執(zhí)行CPU命令呢?
OS X和iOS的核心XNU內(nèi)核在發(fā)生操作系統(tǒng)事件時(shí)會(huì)切換執(zhí)行路徑。執(zhí)行中路徑的狀態(tài),例如CPU的寄存器等信息保存到保存到各自路徑專用的內(nèi)存塊中,從切換目標(biāo)路徑專用的內(nèi)存塊中,復(fù)原CPU寄存器等信息,繼續(xù)執(zhí)行切換路徑的CPU命令列。這被稱為“上下文切換”。
由于使用多線程的程序可以在某個(gè)線程和其他線程之間反復(fù)多次進(jìn)行上下文切換,因此看上去就好像1個(gè)CPU核能夠并列執(zhí)行多個(gè)線程一樣。在多個(gè)CPU核的情況下就真的事提供了多個(gè)CPU核并行執(zhí)行多個(gè)線程的技術(shù)。
這種使用多線程編程的技術(shù)被稱為多“多線程編程”。

=

但是多線程編程實(shí)際上是一種已發(fā)生各種問題的編程技術(shù)。比如多個(gè)線程更新相同的資源會(huì)導(dǎo)致數(shù)據(jù)的不一致(數(shù)據(jù)競(jìng)爭(zhēng))、停止等待事件的線程會(huì)導(dǎo)致多個(gè)線程相互等待(死鎖)、使用太多線程會(huì)消耗大量?jī)?nèi)存等。

=

要回避這些問題有很多辦法,但程序都偏于復(fù)雜。
盡管極易發(fā)生各種問題,也應(yīng)該使用多線程編程。這是因?yàn)槎嗑€程編程可以保證應(yīng)用程序響應(yīng)性能。

1 GCD的API

1.1 dispatch_queue_create

dispatch_queue_create函數(shù)可以生成Dispatch Queue

//生成Serial Dispatch
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.myway.gcd.serial", NULL);
//生成Concurrent Dispatch
dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("com.myway.gcd.Concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_create使用注意事項(xiàng)
  1. 雖然Serial Queue和Concurrent Dispatch Queue受到系統(tǒng)資源的限制,但dispatch_queue_create可生成任意多個(gè)Dispatch Queue。
  2. 生成多個(gè)Serial Dispatch Queue時(shí),多個(gè)Serial Dispatch Queue將并行執(zhí)行。一旦生成Serial Dispatch Queue并追加處理,系統(tǒng)就對(duì)于一個(gè)Serial Dispatch Queue就生成并使用一個(gè)線程。如果過多使用多線程,就會(huì)消耗大量?jī)?nèi)存,引起大量上下文切換,大幅度降低系統(tǒng)的響應(yīng)性能。
  3. 只在為了避免多線程編程問題之一 ----- 多個(gè)線程更新相同資源導(dǎo)致數(shù)據(jù)競(jìng)爭(zhēng)時(shí)使用Serial Dispatch Queue。但是Serial Dispatch Queue的生成數(shù)量應(yīng)當(dāng)僅限所必須的數(shù)量,雖然Serial Dispatch Queue比Concurrent能生成更多的線程,但絕不能激動(dòng)之下生成大量的Serial Queue。當(dāng)想并行執(zhí)行不發(fā)生數(shù)據(jù)競(jìng)爭(zhēng)等問題的處理時(shí),使用Concurrent Dispatch Queue。而且對(duì)于Concurrent Dispatch Queue來說,不管生成多少,由于XNU內(nèi)核只使用有效管理的線程,因此不會(huì)發(fā)生Serial Dispatch Queue的那些問題。

1.2 Main Dispatch Queue 和 Global Dispatch Queue

  1. Main Dispatch Queue 是串行隊(duì)列。 通過dispatch_get_main_queue()獲取
  1. Global Dispatch Queue 是并行隊(duì)列。 通過dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)獲取

Global Dispatch Queue有4個(gè)優(yōu)先級(jí),用于Global Dispatch Queue管理的線程,將各自使用Global Dispatch Queue的優(yōu)先級(jí)作為線程的優(yōu)先級(jí)使用。
但是Global Dispatch Queue的線程并不能保證實(shí)時(shí)性,因此執(zhí)行優(yōu)先級(jí)只是大致判斷。

Main Dispatch Queue 和 Global Dispatch Queue執(zhí)行dispatch_retain和dispatch_release并不會(huì)引起任何變化,也不會(huì)引起任何問題。

1.3 dispatch_set_target_queue

dispatch_queue_create生成的隊(duì)列都是默認(rèn)優(yōu)先級(jí)的Global Dispatch Queue,可以用dispatch_set_target_queue變更優(yōu)先級(jí)。Main Dispatch Queue 和 Global Dispatch Queue不能隨意變更優(yōu)先級(jí),會(huì)引起不可預(yù)知的錯(cuò)誤。
dispatch_set_target_queue不僅可以變更優(yōu)先級(jí),還可以設(shè)置Dispatch Queue的執(zhí)行階層。

1.4 dispatch_after

//ull 是C語言的數(shù)值字面量,是顯示使用類型時(shí)使用的字符串(表示“unsigned long long”)。
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
    dispatch_after(time, dispatch_get_main_queue(), ^{
        NSLog(@"等待至少3秒后執(zhí)行");
    });

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"等待至少3秒后執(zhí)行");
    });

注意:dispatch_after不是在指定時(shí)間后執(zhí)行處理,而是指定時(shí)間后追加處理到Dispatch Queue。
這段源代碼是3秒后追加Block到Main Dispatch Queue。
因?yàn)镸ain Dispatch Queue在主線程的RunLoop中執(zhí)行,所以在比如每隔1/60秒執(zhí)行的RunLoop中,Block最快在3秒后執(zhí)行最慢在3秒+1/60秒后執(zhí)行,如果Main Dispatch Queue有大量處理追加或主線程本身延遲,這個(gè)時(shí)間會(huì)更長(zhǎng)。

dispatch_time_t類型的值可以用 dispatch_time 或 dispatch_walltime函數(shù)作成。

//通過dispatch_walltime用來計(jì)算絕對(duì)時(shí)間
dispatch_time_t getDispatchTimeByDate(NSDate *date){
    NSTimeInterval interval;
    double second, subsecond;
    struct timespec time;
    dispatch_time_t milestone;

    interval = [date timeIntervalSince1970];
    subsecond = modf(interval, &second);
    time.tv_sec = second;
    time.tv_nsec = subsecond * NSEC_PER_SEC;
    milestone = dispatch_walltime(&time, 0);
    return milestone;
}

1.5 dispatch_barrier_async

dispatch_barrier_async(queue, ^{

    });

1.6 Dispatch Group

追加到Dispatch Queue中的多個(gè)處理全部結(jié)束后想執(zhí)行結(jié)束處理,這種情況經(jīng)常出現(xiàn)。
如果是Serial Dispatch Queue只要將想執(zhí)行的處理全部追加到Serial Dispatch Queue中并在最后追加結(jié)束處理,即可實(shí)現(xiàn)。但是使用Concurrent Dispatch Queue時(shí)或同時(shí)使用多個(gè)Dispatch Queue時(shí)代碼就會(huì)變得復(fù)雜,這種情況下就要用到Dispatch Group。

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    for (NSInteger index = 0; index<10; index++) {
        __weak typeof(self) weakSelf = self;
        dispatch_group_async(group, queue, ^{
            [weakSelf setStringValue:[NSString stringWithFormat:@"blk%ld",(long)index]];
        });

    }
//  dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//      NSLog(@"done");
//  });
    long result = dispatch_group_wait(group, DISPATCH_TIME_NOW);
    if (result == 0) {
        //屬于group的全部處理執(zhí)行結(jié)束
        NSLog(@"屬于group的全部處理執(zhí)行結(jié)束");
    }else{
        //屬于group的處理未全部執(zhí)行結(jié)束
        NSLog(@"屬于group的處理未全部執(zhí)行結(jié)束");
    }
dispatch_group_enter(_dispatchGroup);

dispatch_group_leave(_dispatchGroup);

dispatch_group_notify(_dispatchGroup, dispatch_get_main_queue(), ^{
});

1.7 dispatch_apply

dispatch_apply按指定的次數(shù)將指定的Block追加到指定的Dispatch Queue中,并等待全部處理執(zhí)行結(jié)束。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
   dispatch_apply(10, queue, ^(size_t index) {
       NSLog(@"%zu",index);
   });
   NSLog(@"done");//done必定在最后的位置輸出,因?yàn)閐ispatch_apply會(huì)等待全部處理執(zhí)行結(jié)束

因?yàn)閐ispatch_apply會(huì)等待所有處理結(jié)束,所以推薦在dispatch_async中非同步執(zhí)行dispatch_apply

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

   dispatch_async(queue, ^{
       dispatch_apply(10, queue, ^(size_t index) {
           NSLog(@"%zu",index);
       });
       NSLog(@"done");//done必定在最后的位置輸出,因?yàn)閐ispatch_apply會(huì)等待全部處理執(zhí)行結(jié)束
       dispatch_async(dispatch_get_main_queue(), ^{
           //main queue 處理UI等;
       });
   });

1.8 dispatch_suspend/dispatch_resume

```
dispatch_suspend(queue);
dispatch_resume(queue);
```

1.9 dispatch_semaphore_t

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    dispatch_semaphore_signal(semaphore);

GCD總結(jié)

線程是魔鬼,應(yīng)謹(jǐn)慎使用多線程,合理使用多線程可以保證程序運(yùn)行流暢。如果不合理多線程,可能會(huì)引起很多問題,如:大量使用多線程可能會(huì)很快用盡線程棧內(nèi)存。

Dispatch Queues有以下優(yōu)點(diǎn): 他們提供直接而簡(jiǎn)單的編程接口

  1. 他們提供自動(dòng)和完全的線程池管理
  2. 他們提供協(xié)作組件的速度。
  3. 他們更加內(nèi)存高效(因?yàn)榫€程棧不在應(yīng)用的內(nèi)存中,而在內(nèi)核的內(nèi)存中?)
  4. 他們不會(huì)導(dǎo)致內(nèi)核負(fù)載過高
  5. 他們使用dispatch queue異步處理任務(wù),不會(huì)讓queue死鎖
  6. 他們?cè)诟?jìng)爭(zhēng)中保持平和。
  7. 串行dispatch queue提供了一個(gè)比鎖和其他同步機(jī)制更加高效的選擇

你提交給dispatch queue的任務(wù)必須封裝在一個(gè)函數(shù)或者一個(gè)block 對(duì)象中。block對(duì)象是在ios4中的類似于函數(shù)指針但有有額外優(yōu)點(diǎn)的c語言特性。與其定義blocks在他們自己的詞法作用域,你一般定義blocks在他們能訪問的一個(gè)函數(shù)或者方法內(nèi)部。blocks可以移出他們?cè)嫉姆秶缓髲?fù)制到堆上,這就是當(dāng)你將他們提交到dispatch queue上發(fā)生的。所有這些語義使得使得少量代碼就可以實(shí)現(xiàn)非常動(dòng)態(tài)的任務(wù)成為可能。

Dispatch queues是GCD技術(shù)的一部分和C運(yùn)行時(shí)的一部分。

Dispatch Sources

Dispatch Sources是基于C的機(jī)制處理指定類型的系統(tǒng)異步事件。一個(gè)dispatch source封裝包括特定類型的系統(tǒng)事件的信息然后提交給一個(gè)指定的block對(duì)象或者函數(shù)給dispatch queue當(dāng)事件發(fā)生時(shí)。你可以使用dispatch sources來監(jiān)視以下系統(tǒng)事件

定時(shí)器(Timers)

信號(hào)處理(Signal handlers)

描述符相關(guān)的事件(Descriptor-related events)

進(jìn)程相關(guān)事件(Process-related events)

端口事件(Mach port events)

你觸發(fā)的用戶事件(Custom events that you trigger)

Dispache sources是GCD技術(shù)的一部分。

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

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

  • 我們知道在iOS開發(fā)中,一共有四種多線程技術(shù):pthread,NSThread,GCD,NSOperation: ...
    請(qǐng)叫我周小帥閱讀 1,561評(píng)論 0 1
  • 3.1 Grand Central Dispatch(GCD)概要 3.1.1 什么是CGD Grand Cent...
    SkyMing一C閱讀 1,784評(píng)論 0 22
  • 章節(jié)目錄 什么是GCD? 如何在多條路徑中執(zhí)行CPU命令列? 即使多線程存在很多問題(如數(shù)據(jù)競(jìng)爭(zhēng)、死鎖、線程過多消...
    DrunkenMouse閱讀 979評(píng)論 1 13
  • Grand Central Dispatch(GCD)概要 我的博客鏈接 什么是GCD? 蘋果官方這么描述的:Gr...
    換個(gè)名字再說閱讀 1,358評(píng)論 4 7
  • 習(xí)慣只是開始 沒有借口的逃脫 無法伸手惋留 還是享受拋棄的快感 你的眼里全是寵溺 而我只看見了自己眼淚的形狀 你的...
    極度差閱讀 143評(píng)論 0 1

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