分分鐘學會GCD

2014

什麼是GCD

Grand Central Dispatch (GCD)是異步執行任務的技術之一。一般將應用程序中記述的線程管理用的代碼在系統級中實現。由於線程管理是作爲系統的一部分來實現的,因此可統一管理,也可執行任務,這樣就比以前的線程更有效率。

也就是說,GCD用我們難以置信的非常簡潔的記述方法,實現了極爲複雜的多線程編程。

    dispatch_async(queue, ^{
        // 長時間處理
        // 例如數據庫訪問
        // 例如圖像識別
        
        // 長時間處理結束,主線程使用該處理結果
        dispatch_async(dispatch_get_main_queue(), ^{
            // 只在主線程可以執行的處理
            // 例如界面更新
        })
    })

上面的就是在後臺線程中執行長時間處理,處理結束時,主線程使用該處理結果。


多線程編程

雖然CPU的相關技術有很多,但基本上1個CPU內核一次能夠執行的CPU命令始終爲1。那麼怎樣才能在多條路徑中執行CPU命令列呢?

OX X 和 ios 的核心 XNU 內核在發生操作系統事件時(如每隔一定時間,喚起系統調用等情況)會切換路徑。執行中路徑的狀態,例如CPU的寄存器等信息保存到各自路徑專用的內存塊中,從切換目標路徑專用的內存塊中,復原CPU寄存器等信息,繼續執行切換路徑CPU命令列,這被稱爲“上下文切換”。

由於使用多線程的程序可以在某個線程和其他線程之間反覆多次進行上下文切換,因此看上去就好像1個CPU內核能夠並列地執行多個線程一樣。

但是,多線程編程實際上是一種易發生各種問題的編程技術。比如多個線程更新相同的資源會導致數據的不一致,停止等待時間的線程會導致多個線程互相持續等待,使用太多線程會消耗大量內存等。

應用程序在啓動時,通過最先執行的線程,即主進程來描繪用戶界面,處理觸摸屏幕的事件等。如果在該主線程中進行長時間的處理,會妨礙主進程的執行。這就是長時間的處理不在主線程中執行而在其他線程中執行的原因。


GCD 的 API

蘋果官方對 GCD 的說明。

開發者要做的只是定義執行的任務並追加到適當地 Dispatch Queue 中。

    dispatch_async(queue, ^{
       // 想執行的任務
    });
通過 dispatch_async 函數追加賦值在變量 queue 的 Dispatch Queue 中。僅這樣就可以使指定的 Block 在另一線程中執行。

Dispatch Queue 是什麼呢?如其名稱所示,是執行處理的等待隊列。應用程序編程人員通過 dispatch_async 函數等 API, 在 Block 語法中記述想執行的處理並將其追加到 Dispatch Queue 中。按照追加的順序,先進先出執行處理。

另外在執行處理時存在兩種 Dispatch Queue,一種是等待現在執行中處理的 Serial Dispatch Queue,另一種是不等待現在執行中處理的Concurrent Dispatch Queue

那麼如何才能得到這些 Dispatch Queue 呢?方法有兩種。

dispatch_queue_create

第一種方法是通過 GCD 的 API 生成 Dispatch Queue。

dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.example.gcd.mySerialDispatchQueue", NULL);
在說明 dispatch_queue_create 函數之前,先講一下關於 Serial Dispatch Queue 生成個數的注意事項。

如前所訴,Concurrent Dispatch Queue 並行執行多個追加處理,而 Serial Dispatch Queue 同時只能執行1個追加處理。 雖然 Serial Dispatch Queue 和 Concurrent Dispatch Queue 受到系統資源的限制,但用 dispatch_queue_create 函數可生成任意多個 Dispatch Queue。

當生成多個 Serial Dispatch Queue 時,各個 Serial Dispatch Queue 將並行執行。雖然在1個 Serial Dispatch Queue 中同時只能執行一個追加處理,但如果將處理分別追加到4個 Serial Dispatch Queue 中,各個 Serial Dispatch Queue 執行1個,即爲同時執行4個處理

一旦生成 Serial Dispatch Queue 並追加處理,系統對於一個 Serial Dispatch Queue 就只生成並使用一個線程。如果生成 2000 個 Serial Dispatch Queue,那麼就生成 2000 個線程。

像之前列舉的多線程編程問題一樣,如果過多使用多線程,就會消耗大量內存,引起大量的上下文切換,大幅度降低系統的響應性能。

下面我們回來繼續講 dispatch_queue_create 函數。該函數的第一個參數指定 Serial Dispatch Queue 的名稱。如果嫌麻煩設爲 NULL 也可以,但你在調試中一定會後悔沒有爲 Serial Dispatch Queue 署名。

生成 Serial Dispatch Queue 時,像該源代碼這樣,將第二個參數指定爲NULL。生成 Concurrent Dispatch Queue 時,指定爲 DISPATCH_QUEUE_CONCURRENT

dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("com.example.gcd.MyConcurrentDispatchQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_create 函數的返回值爲表示 Dispatch Queue 的 dispatch_queue_t 類型。在之前源代碼中所出現的變量 queue 均爲 dispatch_queue_t 類型變量。

dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("com.example.gcd.MyConcurrentDispatchQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(myConcurrentDispatchQueue, ^{
        NSLog(@"block on myConcurrentDispatchQueue");
    });
該代碼在 Concurrent Dispatch Queue 中執行指定的 Block。

另外,如果你部署的程序跑在 ios6 以上,那麼 ARC 會自動管理,否則需要添加

dispatch_release(myConcurrentDispatchQueue);

Main Dispatch Queue/Global Dispatch Queue

第二種方法是獲取系統標準提供的 Dispatch Queue。

實際上不用特意生成 Dispatch Queue 系統也會給我們提供幾個。那就是 Main Dispatch QueueGlobal Dispatch Queue

Main Dispatch Queue 正如其名稱中含有的 Main 一樣,是在主線程中執行的 Dispatch Queue。因爲主線程只有1個,所以 Main Dispatch Queue 自然就是 Serial Dispatch Queue。

追加到 Main Dispatch Queue 的處理在主線程的 RunLoop 中執行,由於在主線程中執行,因此要將用戶界面的界面更新等一些必須在主線程中執行的處理追加到 Main Dispatch Queue 中使用。

另一個 Global Dispatch Queue 是所有應用程序都能夠使用的 Concurrent Dispatch Queue。沒有必要通過 dispatch_queue_create 函數逐個生成 Concurrent Dispatch Queue。只要獲取 Global Dispatch Queue 使用即可。

另外,Global Dispatch Queue 有4個執行優先級,分別是高優先級,默認優先級,低優先級和後臺優先級。

但是通過 XNU 內核用於 Global Dispatch Queue 的線程並不能保證實時性,因此執行優先級只是大致的判斷。例如在處理內容的執行可有可無時,使用後臺優先級的 Global Dispatch Queue 等,只能進行這種程度的區分。

    // Main Dispatch Queue 的獲取方法
    dispatch_queue_t mainDispatchQueue=dispatch_get_main_queue();
    
    // Global Dispatch Queue (高優先級)的獲取方法
    dispatch_queue_t globalDispatchQueueHigh= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    
    // Global Dispatch Queue (默認優先級)的獲取方法
    dispatch_queue_t globalDispatchQueueDefault= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // Global Dispatch Queue (低優先級)的獲取方法
    dispatch_queue_t globalDispatchQueueLow= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    
    // Global Dispatch Queue (後臺優先級)的獲取方法
    dispatch_queue_t globalDispatchQueueBackground= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
以下舉例了使用 Main Dispatch QueueGlobal Dispatch Queue 的代碼。

    // 在默認優先級的 Global Dispatch Queue 中執行 Block
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       // 並行執行的處理
        
       // 在 Main Dispatch Queue 中執行 Block
        dispatch_async(dispatch_get_main_queue(), ^{
           // 只能在主線程中執行的處理
        });
    });

dispatch_set_target_queue

dispatch_set_target_queue 函數生成的 Dispatch Queue 不管是 Serial Dispatch Queue 還是 Concurrent Dispatch Queue,都使用與默認優先級 Global Dispatch Queue 相同執行優先級的線程。而變更生成的Dispatch Queue 的執行優先級要使用 dispatch_set_target_queue 函數。在後臺執行動作處理的 Serial Dispatch Queue 的生成方法如下。

    dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.example.gcd.mySerialDispatchQueue", NULL);
    dispatch_queue_t globalDispatchQueueBackground= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    dispatch_set_target_queue(mySerialDispatchQueue, globalDispatchQueueBackground);
指定要變更執行優先級的 Dispatch Queue 爲 dispatch_set_target_queue 函數的第一個參數,指定與要使用的執行優先級相同優先級的 Global Dispatch Queue 爲第二個參數。第一個參數不能指定系統提供的 Main Dispatch Queue 和 Global Dispatch Queue

將 Dispatch Queue 指定爲 dispatch_set_target_queue 函數的參數,不僅可以變更 Dispatch Queue 的執行優先級,還可以作成 Dispatch Queue 的執行階層。如果在多個 Serial Dispatch Queue 中用 dispatch_set_target_queue 函數指定目標爲某一個 Serial Dispatch Queue,那麼原先本應並行執行的多個 Serial Dispatch Queue,在目標 Serial Dispatch Queue 上只能同時執行一個處理

在必須將不可並行執行的處理追加到多個 Serial Dispatch Queue 中時,如果使用 dispatch_set_target_queue 函數將目標指定爲某一個 Serial Dispatch Queue,即可防止處理並行執行。

dispatch_after

經常會有這樣的情況:想在3秒後執行處理。這種想在指定時間後執行處理的情況,可使用 dispatch_after 函數來實現。

在3秒後將指定的 Block 追加到 Main Dispatch Queue 中的源代碼如下:

    dispatch_time_t time=dispatch_time(DISPATCH_TIME_NOW, 3ull*NSEC_PER_SEC);
    dispatch_after(time, dispatch_get_main_queue(), ^{
        NSLog(@"waited at least three seconds.");
    });
要注意是,dispatch_after 函數並不是在指定時間後執行處理,而只是在指定時間追加處理到 Dispatch Queue。此源代碼與在3秒後用 dispatch_async 函數追加 Block 到 Main Dispatch Queue 的相同。

因爲 Main Dispatch Queue 在主線程的 RunLoop 中執行,比如每隔1/60秒執行的 RunLoop 中,Block 最快在3秒後執行,最慢在3秒+1/60秒後執行,並且在 Main Dispatch Queue 有大量處理追加或主線程的處理本身有延時時,這個時間會更長。

雖然在有嚴格時間的要求下使用時會出現問題,但在想大致延遲執行處理時,該函數是非常有效的。

第二個參數指定要追加處理的 Dispatch Queue,第三個參數指定記述要執行處理的 Block。

第一個參數是指定時間用的 dispatch_time_t 類型的值。該值使用 dispatch_time 函數或者 dispatch_walltime 函數作成。

dispatch_time 函數能夠獲取從第一個參數 dispatch_time_t 類型值中指定的時間開始,到第二個參數指定的毫微秒單位時間後的時間。第一個參數經常使用的值是之前源代碼中出現的 DISOATCH_TIME_NOW。表示現在的時間。

數值和 NSEC_PER_SEC 的乘積得到單位爲毫微秒的數值。ull 是C語言的數值字面量,表示 unsigned long long。如果使用 NSEC_PER_MSEC 則可以以毫秒爲單位計算。

Dispatch Group

在追加到 Dispatch Queue 中的多個處理全部結束後想執行結束處理,這種情況會經常出現。只使用一個 Serial Dispatch Queue 時,只要將想執行的處理全部追加到該 Serial Dispatch Queue 中並在最後追加結束處理,即可實現。但是在使用 Concurrent Dispatch Queue 時或同時使用多個 Dispatch Queue 時,源代碼會變得頗爲複雜。

在此情況下使用 Dispatch Group。例如下面代碼爲:追加3個 Block 到 Global Dispatch Queue,這些 Block 如果全部執行完畢,就會執行 Main Dispatch Queue 中結束處理用的 Block。

    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, ^{
        NSLog(@"blk0");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"blk1");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"blk2");
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"done");
    });
執行結果如下:

2014-10-06 14:49:29.929 GCD[2197:111113] blk2
2014-10-06 14:49:29.929 GCD[2197:111112] blk1
2014-10-06 14:49:29.929 GCD[2197:111111] blk0
2014-10-06 14:49:29.941 GCD[2197:111019] done
因爲向 Global Dispatch Queue 即 Concurrent Dispatch Queue 追加處理,多個線程並行執行,所以追加處理的執行順序不定。執行時會發生變化,但是此執行結果的 done 一定是最後輸出的。

無論向什麼樣的 Dispatch Queue 中追加處理,使用 Dispatch Group 都可監視這些處理執行的結束。一旦檢測到所有處理執行結束,就可將結束的處理追加到 Dispatch Queue 中。這就是使用 Dispatch Group 的原因。

首先 dispatch_group_create 函數生成 dispatch_group_t 類型的 Dispatch Group。 如 dispatch_group_create 函數名所含的 create 所示。

dipatch_group_async 函數與 dispatch_async 函數相同,都追加 Block 到指定的 Dispatch Queue 中。與 dispatch_async 函數不同的是指定生成的 Dispatch Group 爲第一個參數。指定的 Block 屬於指定的 Dispatch Group。

在追加到 Dispatch Group 中的處理全部執行結束時,該源碼中使用的 dispatch_group_notify 函數會將執行的 Block 追加到 Dispatch Queue 中,將第一個參數指定爲想要監視的 Dispatch Group。在追加到該 Dispatch Group 的全部處理執行結束時,將第三個參數的 Block 追加到第二個參數的 Dispatch Queue 中。在 dispatch_group_notify 函數中不管指定什麼樣的 Dispatch Queue,屬於 Dispatch Group 的全部處理在追加指定的 Block 時都已執行結束。

另外,在 Dispatch Group 中也可以使用 dispatch_group_wait 函數僅等待全部處理執行結束。

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, ^{
        NSLog(@"blk0");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"blk1");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"blk2");
    });
    
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

dispatch_group_wait 函數的第二個參數指定爲等待的時間(超時),它屬於 dispatch_time_t 類型的值。DISPATCH_TIME_FOREVER,意味着永久等待。只要屬於 Dispatch Group 的處理尚未執行結束,就會一直等待,中途不能取消。

如同 dispatch_after 函數說明中出現的那樣,指定等待間隔爲1秒時應該做如下處理。

dispatch_time_t time=dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC);
    long result=dispatch_group_wait(group, time);
    if (result==0) {
        // 全部處理結束
    }else{
        // 某一個處理還在執行
    }

如果 dispatch_group_wait 函數的返回值不爲0,就意味着雖然經過了指定時間,但屬於 Dispatch  Group 的某一個處理還在執行中。如果返回值爲0,那麼全部處理執行結束。當等待時間爲 DISPATCH_TIME_FOREVER,由 dispatch_group_wait 函數返回時,由於屬於 Dispatch Group 的處理必定全部執行結束,因此返回值恆爲0。

指定 DISPATCH_TIME_NOW,則不用任何等待即可以判斷屬於 Dispatch Group 的處理是否執行結束。

long result=dispatch_group_wait(group,DISPATCH_TIME_NOW)
在主線程的 RunLoop 的每次循環中,可檢查執行是否結束,從而不消耗多餘的等待時間,雖然這樣也可以,還是推薦使用 dispatch_group_notify 函數追加結束處理到 Main Dispatch Queue 中。這是因爲 dispatch_group_notify 函數可以簡化源代碼。

dispatch_barrier_async

在訪問數據庫或者文件時,如前所述,使用 Serial Dispatch Queue 可以避免數據競爭的問題。
寫入處理確實不可與其他的寫入處理以及包含讀取處理的其他某些處理並行執行。但是如果讀取處理只是與讀取處理並行執行,那麼多個並行執行就不會發生問題。

也就是說,爲了高效率地進行訪問,讀取處理追加到 Concurrent Dispatch Queue 中,寫入處理在任一個讀取處理沒有執行的狀態下,追加到 Serial Dispatch Queue 中即可。

雖然利用 Dispatch Group 和 dispatch_set_target_queue 函數也可實現,但是源代碼會很複雜。

GCD 爲我們提供了更爲聰明的解決辦法-dispatch_barrier_async 函數。該函數和 dispatch_queue_create 函數生成的 Concurrent Dispatch Queue 一起使用。

首先 dispatch_queue_create 函數生成 Concurrent Dispatch Queue,在 dispatch_async 中追加讀取處理。

    dispatch_queue_t queue=dispatch_queue_create("com.example.gcd.ForBarrier", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, blk0_for_reading);
    dispatch_async(queue, blk1_for_reading);
    dispatch_async(queue, blk2_for_reading);
    dispatch_async(queue, blk3_for_reading);
在 blk1 和 blk2 之間執行寫入處理,並將寫入的內容讀取 blk2 處理以及之後的處理中。
    dispatch_queue_t queue=dispatch_queue_create("com.example.gcd.ForBarrier", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, blk0_for_reading);
    dispatch_async(queue, blk1_for_reading);
    
    dispatch_barrier_async(queue, blk_for_writing);
    
    dispatch_async(queue, blk2_for_reading);
    dispatch_async(queue, blk3_for_reading);
如上所示,使用方法非常簡單,使用 dispatch_barrier_async 函數代替 dispatch_async 函數即可。

dispatch_sync

dispatch_async 函數的 async 意味着 非同步,就是將指定的 Block 非同步 地追加到指定的 Dispatch Queue 中。dispatch_async 函數不做任何等待。

既然有 async,當然有 sync,在追加 Block 結束之前,dispatch_sync 函數會一直等待。

我們先假設這樣一種情況:執行 Main Dispatch Queue 時,使用另外的線程 Global Dispatch Queue 進行處理,處理結束後立即使用所得到的結果。在這種情況下使用 dispatch_sync 函數。

    dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_sync(queue, ^{
        NSLog(@"處理");
    });
另外,由 dispatch_barrier_async 函數中含有 async 可推測出,相應地也有 dispatch_barrier_sync 函數。dispatch_barrier_async 函數的作用是等待追加的處理全部執行結束後,再追加處理到 Dispatch Queue 中,此外,它還與 dispatch_sync 函數相同,會等待追加處理的執行結束。

dispatch_apply

dispatch_apply 函數是 dispatch_sync 函數和 Dispatch Group 的關聯 API。該函數按指定的次數將指定的 Block 追加到指定的 Dispatch Queue 中,並等待全部處理執行結束

    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");
執行結果爲

2014-10-07 10:18:18.510 GCD[612:60b] 0
2014-10-07 10:18:18.510 GCD[612:3503] 2
2014-10-07 10:18:18.513 GCD[612:60b] 4
2014-10-07 10:18:18.510 GCD[612:3403] 3
2014-10-07 10:18:18.513 GCD[612:60b] 6
2014-10-07 10:18:18.514 GCD[612:60b] 8
2014-10-07 10:18:18.514 GCD[612:60b] 9
2014-10-07 10:18:18.513 GCD[612:3503] 5
2014-10-07 10:18:18.510 GCD[612:1303] 1
2014-10-07 10:18:18.513 GCD[612:3403] 7
2014-10-07 10:18:18.515 GCD[612:60b] done
因爲在 Global Dispatch Queue 中執行處理,所以各個處理的執行時間不定。但是輸出結果中最後的 done 必定在最後的位置上。這是因爲 dispatch_apply 函數會等待全部處理執行結束。

第一個參數爲重複次數,第二個參數爲追加對象的 Dispatch Queue,第三個參數爲追加的處理。與到目前爲止所出現的例子不同,第三個參數的 Block 爲帶有參數的 Block。這是爲了按第一個參數重複追加 Block 並區分各個 Block 而使用。

dispatch_suspend/dispatch_resume

當追加大量處理到 Dispatch Queue 時,在追加處理的過程中,有時希望不執行已追加的處理。

在這種情況下,只要掛起 Dispatch Queue 即可。當可以執行時再恢復。

掛起的函數

dispatch_suspend(queue);
恢復的函數

dispatch_resume(queue);
這些函數對已經執行的處理沒有影響。掛起後,追加到 Dispatch Queue 中但尚未執行的處理在此之後停止執行。而恢復則使得這些處理能夠繼續執行。

dispatch_once

dispatch_once 函數是保證在應用程序執行中只執行一次指定處理的 API。下面這種經常出現的用來進行初始化的源代碼可通過 dispatch_once 函數簡化。

    static int initialized = NO;
    if (initialized==NO) {
        //初始化
        initialized=YES;
    }
如果使用 dispatch_once 函數,則源代碼爲

    static dispatch_once_t pred;
    dispatch_once (&pred,^{
        //初始化
    });
源代碼看起來沒有太大變化,但是通過 dispatch_once 函數,該源代碼即使在多線程環境下執行,也可保證百分之百安全。
之前的代碼在大多數情況下也是安全的,但是在多核 CPU 中,在正在更新表示是否初始化的標誌變量時讀取,就有可能多次執行初始化處理。

GCD 實現

Dispatch Source

GCD 中除了主要的 Dispatch Queue 外,還有不太引人注目的 Disaptch Source。它是 BSD 系內核慣有功能 kqueue 的包裝。

kqueue 是在 XNU 內核中發生各種事件時,在應用程序編程方執行處理的技術。其 CPU 負荷非常小,儘量不佔用資源。

使用 DISPATCH_SOURCE_TYPE_TIMER 的定時器的例子。在網絡編程的通信超時等情況下可以使用該例子。

// 指定 DISPATCH_SOURCE_TYPE_TIMER, 作爲 Dispatch Source
    // 在定時器經過指定時間時設定 Main Dispatch Queue 爲追加處理的 Dispatch Queue
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
    
    // 將定時器設爲2秒後
    // 不指定爲重複
    // 允許延時1秒
    dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 2ull * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 1ull * NSEC_PER_SEC);
    
    // 指定定時器指定時間內執行的處理
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"wakeup!");
        dispatch_source_cancel(timer);
    });
    
    // 指定取消 Dispatch Source 時的處理
    dispatch_source_set_cancel_handler(timer, ^{
        NSLog(@"canceled");
        //dispatch_release(timer);
    });
    
    // 啓動Dispatch Source
    dispatch_resume(timer);
實際上 Dispatch Queue 沒有“取消”這一概念。一旦將處理追加到 Dispatch Queue 中,就沒有方法可以將該處理去除,也沒有方法可以在執行中取消該處理。如果要取消,考慮 NSOperationQueue 等其他方法。
Dispatch Source 與 Dispatch Queue 不同,是可以取消的。而且取消時必須執行的處理可指定爲回調的Block形式。因此使用 Dispatch Source 實現 XNU 內核中發生的事件處理要比直接使用 kqueue 實現更爲簡單。

example

http://download.csdn.net/detail/gwh111/8008367



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章