GCD

GCD


OS X 10.8或iOS 6以及以後版本中使用,Dispatch Queue將會由ARC自動管理,不須要手動釋放安全

隊列

分爲串行隊列和併發隊列併發

  • 將多個任務提交給串行隊列,多個任務只能按順序執行,前一個任務執行完,才能開始下一個任務
  • 將多個任務交給併發隊列,併發隊列能夠按FIFO的順序啓動多個任務,任務完成順序按任務和系統決定

獲取隊列:app

  • dispatch_get_main_queue()
    • 獲取主線程關聯串行隊列
  • dispatch_get_current_queue()
    • 獲取當前執行代碼所在隊列
  • dispatch_get_global_queue(long identifier, unsigned long flags)
    • 獲取系統的全局併發隊列
    • 第一個參數接受一下四個優先級
      • DISPATCH_QUEUE_PRIORITY_HIGH:
      • DISPATCH_QUEUE_PRIORITY_DEFAULT:
      • DISPATCH_QUEUE_PRIORITY_LOW:
      • DISPATCH_QUEUE_PRIORITY_BACKGROUND:
    • 第二個參數通常傳入0

建立隊列:異步

  • dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
    • 第一個參數表示隊列對應字符串標籤
    • 第二個參數指定隊列類型,分爲:
      • DISPATCH_QUEUE_SERIAL 串行
      • DISPATCH_QUEUE_CONCURRENT 併發

獲取隊列相關信息async

  • dispatch_queue_get_label(dispatch_queue_t queue);
    • 獲取隊列對應的標籤

提交任務

  • dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
    • 將代碼塊以異步方式提交給指定隊列
  • dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
    • 將代碼塊以同步方式提交給指定隊列
    • 前後提交的兩個代碼塊(即便提交給併發隊列),前一個執行完纔會執行下一個
  • dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
    • 將代碼塊以異步方式提交給指定隊列,並在dispatch_time指定的時間開始執行
  • dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));
    • 將代碼塊以異步方式提交給指定隊列,重複執行代碼
    • 第一個參數指定重複幾回
    • 第三個參數 block代碼塊的 size_t表示當前正在執行第幾回
  • dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
    • 在任務提交給隊列,在應用的某個生命週期內只執行一次
    • 第一個參數表示代碼塊是否已經執行過

dispatch_group

  • void dispatch_group_notify(dispatch_group_t group,
    dispatch_queue_t queue,
    dispatch_block_t block);
    • group中全部代碼塊執行完以後執行
  • long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);
    • 返回值表示通過指定的等待時間,屬於這個group的任務是否已經所有執行完,若是是則返回0,不然返回非0。
    • 第一個參數表示等待的group
    • 第二個參數則表示等待時間,有兩個特殊值
      • DISPATCH_TIME_NOW 表示馬上檢查屬於這個group的任務是否已經完成
      • DISPATCH_TIME_FOREVER 表示一直等到屬於這個group的任務所有完成。

例子:ide

let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
let group = dispatch_group_create()

dispatch_group_async(group, globalQueue) { () -> Void in
println("1")
}
dispatch_group_async(group, globalQueue) { () -> Void in
println("2")
}
dispatch_group_async(group, globalQueue) { () -> Void in
println("3")
}
dispatch_group_notify(group, globalQueue) { () -> Void in
println("completed")
}

輸出的順序與添加進隊列的順序無關,由於隊列是Concurrent Dispatch Queue,但「completed」的輸出必定是在最後的函數

let globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
let group = dispatch_group_create()

dispatch_group_async(group, globalQueue) { () -> Void in
println("1")
}
dispatch_group_async(group, globalQueue) { () -> Void in
println("2")
}
dispatch_group_async(group, globalQueue) { () -> Void in
println("3")
}
//使用dispatch_group_wait函數
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
println("completed")

暫停和恢復

這些函數不會影響到隊列中已經執行的任務,隊列暫停後,已經添加到隊列中但尚未執行的任務不會執行,直到隊列被恢復spa

  • dispatch_suspend(queue) //暫停某個隊列
  • dispatch_resume(queue) //恢復某個隊列

dispatch_barrier_async

咱們知道數據在寫入時,不能在其餘線程讀取或寫入。可是多個線程同時讀取數據是沒有問題的。因此咱們能夠把讀取任務放入並行隊列,把寫入任務放入串行隊列,而且保證寫入任務執行過程當中沒有讀取任務能夠執行。線程

這樣的需求比較常見,GCD提供了一個很是簡單的解決辦法——dispatch_barrier_asynccode

假設咱們有四個讀取任務,在第二三個任務之間有一個寫入任務,代碼大概是這樣:

let queue = dispatch_queue_create("com.gcd.kt", DISPATCH_QUEUE_CONCURRENT)

dispatch_async(queue, block1_for_reading)  
dispatch_async(queue, block2_for_reading)

/*
    這裏插入寫入任務,好比:
    dispatch_async(queue, block_for_writing)
*/

dispatch_async(queue, block3_for_reading)  
dispatch_async(queue, block4_for_reading)

若是代碼這樣寫,因爲這幾個block是併發執行,就有可能在前兩個block中讀取到已經修改了的數據。若是是有多寫入任務,那問題更嚴重,可能會有數據競爭。

若是使用dispatch_barrier_async函數,代碼就能夠這麼寫:

dispatch_async(queue, block1_for_reading)  
dispatch_async(queue, block2_for_reading)

dispatch_barrier_async(queue, block_for_writing)

dispatch_async(queue, block3_for_reading)  
dispatch_async(queue, block4_for_reading)

dispatch_barrier_async會把並行隊列的運行週期分爲這三個過程:

  1. 首先等目前追加到並行隊列中全部任務都執行完成
  2. 開始執行dispatch_barrier_async中的任務,這時候即便向並行隊列提交任務,也不會執行
  3. dispatch_barrier_async中的任務執行完成後,並行隊列恢復正常。

總的來講,dispatch_barrier_async起到了「承上啓下」的做用。它保證此前的任務都先於本身執行,此後的任務也遲於本身執行。正如barrier的含義同樣,它起到了一個柵欄、或是分水嶺的做用。

這樣一來,使用並行隊列和dispatc_barrier_async方法,就能夠高效的進行數據和文件讀寫了。


信號量

dispatch_semaphore

首先介紹一下信號量(semaphore)的概念。信號量是持有計數的信號,不過這麼解釋等於沒解釋。咱們舉個生活中的例子來看看。

假設有一個房子,它對應進程的概念,房子裏的人就對應着線程。一個進程能夠包括多個線程。這個房子(進程)有不少資源,好比花園、客廳等,是全部人(線程)共享的。

可是有些地方,好比臥室,最多隻有兩我的能進去睡覺。怎麼辦呢,在臥室門口掛上兩把鑰匙。進去的人(線程)拿着鑰匙進去,沒有鑰匙就不能進去,出來的時候把鑰匙放回門口。

這時候,門口的鑰匙數量就稱爲信號量(Semaphore)。很明顯,信號量爲0時須要等待,信號量不爲零時,減去1並且不等待。

在GCD中,建立信號量的語法以下:

var semaphore = dispatch_semaphore_create(2)

這句代碼經過dispatch_semaphore_create方法建立一個信號量並設置初始值爲2。而後就能夠調用dispatch_semaphore_wait方法了。

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)

dispatch_semaphore_wait方法表示一直等待直到信號量的值大於等於一,當這個方法執行後,會把第一個信號量參數的值減1。

第二個參數是一個dispatch_time_t類型的時間,它表示這個方法最大的等待時間。這在第一章中已經講過,好比
DISPATCH_TIME_FOREVER表示永久等待。

返回值也和dispatch_group_wait方法同樣,返回0表示在規定的等待時間內第一個參數信號量的值已經大於等於1,不然表示已超過規定等待時間,但信號量的值仍是0。

dispatch_semaphore_wait方法返回0,由於此時的信號量的值大於等於一,任務得到了能夠執行的權限。這時候咱們就能夠安全的執行須要進行排他控制的任務了。

任務結束時還須要調用
dispatch_semaphore_signal()方法,將信號量的值加1。這相似於以前所說的,從臥室出來要把鎖放回門上,不然後來的人就沒法進入了。

咱們來看一個完整的例子:

var semaphore = dispatch_semaphore_create(1)  
let queue = dispatch_queue_create("com.gcd.kt", DISPATCH_QUEUE_CONCURRENT)  
var array: [Int] = []

for i in 1...100000 {  
    dispatch_async(queue, { () -> Void in
    /*
        某個線程執行到這裏,若是信號量值爲1,那麼wait方法返回1,開始執行接下來的操做。
        與此同時,由於信號量變爲0,其它執行到這裏的線程都必須等待
    */
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)

    /*
        執行了wait方法後,信號量的值變成了0。能夠進行接下來的操做。
        這時候其它線程都得等待wait方法返回。
        能夠對array修改的線程在任意時刻都只有一個,能夠安全的修改array
    */
    array.append(i)

    /*
        排他操做執行結束,記得要調用signal方法,把信號量的值加1。
        這樣,若是有別的線程在等待wait函數返回,就由最早等待的線程執行。
    */
    dispatch_semaphore_signal(semaphore)
})
}

計時器

NSTimeInterval period = 1.0; //設置時間間隔

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);

dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), period * NSEC_PER_SEC, 0); //每秒執行

dispatch_source_set_event_handler(_timer, ^{

//在這裏執行事件

});

dispatch_resume(_timer);
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息