GCD
是蘋果異步執行任務技術,將應用程序中的線程管理的代碼在系統級中實現。開發者只須要定義想要執行的任務並追加到適當的Dispatch Queue
中,GCD
就能生成必要的線程並計劃執行任務。因爲線程管理是做爲系統的一部分來實現的,所以能夠統一管理,也可執行任務,這樣比之前的線程更有效率。objective-c
dispatch_sync synchronous
同步,一旦調用dispatch_sync
方法,那麼指定的處理(block)
追加到指定Dispatch Queue
中在執行結束以前該函數都不會返回,也就是說當前的線程會阻塞,等待dispatch_sync
在指定線程執行完成後纔會繼續向下執行。 數據庫
dispatch_async
synchronous異步,一旦調用dispatch_async
方法,那麼指定的處理(block)
追加到指定的Dispatch Queue
中,dispatch_async
不會作任何等待馬上返回,當前線程不受影響繼續向下執行。 併發
注意
使用dispatch_sync
容易形成死鎖,通常狀況下應該使用dispatch_async
,例如app
dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_sync(queue, ^{ NSLog(@"1"); }); NSLog(@"2"); //這行代碼不會輸出
由於主線程等待執行結束,而又要在主線程中執行。因此形成了死鎖下面的代碼不會執行。通常狀況下應使用。dispatch_syncdispatch_syncblockdispatch_async
Dispatch Queue
是執行處理的等待隊列,經過Block
把想要執行的處理追加到Dispatch Queue
中,根據追加的順序經過FIFO(先進先出)來執行處理。
有兩種類型的隊列,一種是Serial Dispatch Queue
(串行隊列)等待正在執行中的處理當處理結束時再執行隊列中下一個處理。一種是Concurrent Dispatch Queue
(併發隊列)不等待如今執行中的處理。異步
Dispatch Queue種類 | 說明 |
---|---|
Serial Dispatch Queue | 等待如今執行中的處理結束 |
Concurrent Dispatch Queue | 不等待如今執行中的處理結束 |
用代碼詳細說明兩種隊列async
dispatch_async(queue,block0); dispatch_async(queue,block1); dispatch_async(queue,block2); dispatch_async(queue,block3); dispatch_async(queue,block4);
當queue
爲Serial Dispatch Queue
時輸出函數
block0 block1 block2 block3 block4
當queue
爲Concurrent Dispatch Queue
時輸出oop
block1 block0 block2 block4 block3
Serial Dispatch Queue
當上面的queue
爲Serial Dispatch Queue
時按順序執行,先執行block0
執行結束後執行block1
、block2
依次類推,由於是串行執行因此係統此時只開闢了一個線程來處理 ui
Concurrent Dispatch Queue
當上面的queue
爲Concurrent Dispatch Queue
時,由於不用等待執行中的處理結束,因此首先執行block0
,無論block0
是否結束都開始執行後面的block1
,不等block1
執行結束都執行block2
依次類推。覺得是併發執行,其實是開闢了多個線程同時執行多個處理。 spa
關於Concurrent Dispatch Queue線程問題 Concurrent Dispatch Queue
中並行處理根據Dispatch Queue中的處理數量
,機器CPU負載等一些狀態來決定應該開闢多少個線程來處理。假如此時只能開闢三個線程可是要處理5個block
事件系統應是下面這樣處理:
線程0 | 線程1 | 線程2 |
---|---|---|
block0 | block1 | block2 |
block3 | block4 |
此時線程0處理block0
,線程1處理block1
,線程2處理block2
。當block0
執行玩後執行block3
,可能此時block1
尚未執行完,只有block1
執行結束後纔會執行block4。因此說併發隊列執行的順序是不肯定的。
系統已經爲咱們提供了幾種Dispatch Queue
,不用咱們去主動建立。Main Dispatch Queue
把處理追加到當前的主線程RunLoop中執行。Global Dispatch Queue
是把處理追加到一個Concurrent Dispatch Queue
隊列中處理。Global Dispatch Queue
有四個優先級,系統提供的Dispatch Queue
以下表所示:
名稱 | Dispatch Queue 的種類 | 說明 |
---|---|---|
Main Dispatch Queue | Serial Dispatch Queue | 主線程執行 |
Global Dispatch Queue (High Priority) | Concurrent Dispatch Queue | 執行優先級(高) |
Global Dispatch Queue (Default Priority) | Concurrent Dispatch Queue | 執行優先級(默認) |
Global Dispatch Queue (Low Priority) | Concurrent Dispatch Queue | 執行優先級(低) |
Global Dispatch Queue (Background Priority) | Concurrent Dispatch Queue | 執行優先級(後臺) |
dispatch_queue_t mainQueue = dispatch_get_main_queue(); dispatch_queue_t globalQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); dispatch_queue_t globalQueueDefau = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_queue_t globalQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); dispatch_queue_t globalQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
本身經過dispatch_queue_creat
函數建立的Dispatch Queue
不論是串行隊列仍是併發的隊列的優先級都是與Global Dispatch Queue
的默認優先級相同,使用dispatch_set_target_queue
能夠變動Dispatch Queue
的優先級 dispatch_set_target_queue
使用
dispatch_queue_t serialQueue = dispatch_queue_create("com.test.serialQueue", NULL); dispatch_queue_t globalQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); dispatch_set_target_queue(serialQueue, globalQueueHigh);
上面的代碼實現了經過建立的串行隊列優先級默認變成了最高優先級,實現的效果是當有多個默認優先級的併發執行時,若是設置了某一個優先級爲最高,那麼先執行這個最高優先級的隊列,而後再併發執行其餘優先級相同的隊列。dispatch_queue_creatSerial Dispatch QueueSerial Dispatch Queue
若是想在某一時間後執行某一操做,實現定時器的效果能夠用dispatch_after
來實現
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC); //從當前時間開始的10秒後 dispatch_after(time, dispatch_get_main_queue(), ^{ NSLog(@""); }); //將10秒後將要執行的操做追加到主線程進行執行
注意
上面的代碼中dispatch_after
並非在10秒以後執行某一操做,而是在10秒後把要執行的操做追加到主線程中。好比主線程每0.01秒執一次RunLoop
,那麼這個追加操做最快10秒執行,最慢10+0.01秒執行。
上面介紹到若是使用Concurrent Dispatch Queue
的話是不能肯定隊列中任務的執行順序的,若是Concurrent Dispatch Queue
中有三個任務要在這三個任務都執行結束後進行某個操做,這時就須要用到Dispatch Group
。
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(@"block0"); }); dispatch_group_async(group, queue, ^{ NSLog(@"block1"); }); dispatch_group_async(group, queue, ^{ NSLog(@"block2"); }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"Finish"); });
代碼執行結果以下
block2 block1 block0 Finish
當追加到Dispatch Group
中的處理所有結束時,dispatch_group_notify
將會執行追加的Block。
與dispatch_group_notify
相似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(@"block0"); }); dispatch_group_async(group, queue, ^{ NSLog(@"block1"); }); dispatch_group_async(group, queue, ^{ NSLog(@"block2"); }); dispatch_group_wait(group, DISPATCH_TIME_FOREVER); NSLog(@"Finish");
執行結果以下
block2 block1 block0 Finish
dispatch_group_wait
的效果是等待group
追加的操做所有執行完後再執行下面的代碼,第二個參數表示等待的時間,DISPATCH_TIME_FOREVER
表示一直等待group
的處理結果。直處處理完成才執行下面的代碼。
若是隻想等待一段指定的時間的話改變DISPATCH_TIME_FOREVER
便可
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC); long result = dispatch_group_wait(group, time); if (result == 0) { NSLog(@"Finish"); } else{}
上面代碼表示只等待1秒無論group
中的處理是否所有完成都要執行下面的代碼,當result = 0
表示group
中的處理已經處理完成,不然沒有完成。
若是在Concurrent Dispatch Queue
中追加五個操做,這時想先併發執行前三個操做,等前三個操做都執行結束後再併發的執行後兩個操做,這時就須要用到dispatch_barrier_async
函數,具體實現以下:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ NSLog(@"block0"); }); dispatch_async(queue, ^{ NSLog(@"block1"); }); dispatch_async(queue, ^{ NSLog(@"block2"); }); dispatch_barrier_async(queue, ^{ NSLog(@"barrier"); }); dispatch_async(queue, ^{ NSLog(@"block3"); }); dispatch_async(queue, ^{ NSLog(@"block4"); });
代碼執行結果以下
block1
block0
block2
barrier block3 block4
使用dispatch_barrier_async
會等待在它以前追加到Concurrent Dispatch Queue
中的全部操做都執行結束以後,再執行在它以後追加到Concurrent Dispatch Queue
中的操做。
Dispatch Group
是等待追加到它隊列裏面的全部操做執行結束。而dispatch_barrier_async
是等待在它以前追加到它對列裏面的操做。一個是等待隊列執行結束,一個是等待隊列中某些操做執行結束。
dispatch_apply
是按照指定的次數把操做(block)
追加到指定的Dispatch Group
中
示例1
dispatch_queue_t queueSerial = dispatch_queue_create("com.myProject.queueSerial", NULL); dispatch_apply(5, queueSerial, ^(size_t index) { NSLog(@"%zu",index); });
運行結果
0 1 2 3 4
示例2
dispatch_queue_t queueCurrnt = dispatch_queue_create("com.myProject.queueCurrnt", DISPATCH_QUEUE_CONCURRENT); dispatch_apply(5, queueCurrnt, ^(size_t index) { NSLog(@"%zu",index); });
運行結果
3 0 1 2 4
dispatch_apply
第一個參數是要向隊列中追加幾回操做,第二個參數是將要追加操做的次數,第三個參數是用來區分第幾回追加的操做。示例1
中是向串行隊列Serial Dispatch Queue
追加操做。示例2
中是向併發隊列Concurrent Dispatch Queue
追加操做。
在上面的示例中用到了dispatch_queue_create
函數這個是用來建立Serial Dispatch Queue
與Concurrent Dispatch Queue
。這個函數第一個參數
是隊列的標識符,標識符的寫法最好按照域名倒寫的方法來表示Dispatch Queue
,這樣方便在調試中查看。第二個參數
表示建立隊列的類型當爲NULL
表示建立串行隊列,DISPATCH_QUEUE_CONCURRENT
表示建立並行隊列。
若是再進行某個操做時,不想執行隊列中的操做,在這個操做完成時再執行隊列中的操做,這時用dispatch_suspend
會掛起當前的隊列,此時不會執行隊列中追加的操做。而用dispatch_resume
會恢復掛起的隊列。dispatch_suspend
與dispatch_resume
必須成對調用,有掛起就應該有恢復。
dispatch_queue_t queue = dispatch_queue_create("com.myProject.queueCurrnt", NULL); dispatch_suspend(queue); dispatch_async(queue, ^{ for (unsigned int i = 0; i<10; i++) { NSLog(@"Concurrent"); } }); for (unsigned int i = 0; i<10; i++) { NSLog(@"Serial"); } dispatch_resume(queue);
若是沒有dispatch_suspend
與dispatch_resume
那麼"Concurrent"
與"Serial"
會交替的輸出,若是使用dispatch_suspend
會把隊列掛起而後執行下面的代碼當"Serial"
所有輸出以後dispatch_resume
恢復隊列開始輸出"Concurrent"
。
當用Concurrent Dispatch Queue
對數據庫操做時容易發生數據競爭,當有100條數據要進行寫入操做時,由於是併發操做若是此時正在寫入第1條數據,同時第3條數據也要寫入。這時程序就會發生錯誤,用Dispatch Semaphore
能夠解決這種問題,固然Dispatch Semaphore
不只僅侷限於此。Dispatch Semaphore
相似於信號等待,當對一個對象進行一項操做時,在這操做期間不容許其餘操做來訪問對象Dispatch Semaphore
會設置一道屏障來阻止其餘操做,到操做完成後向Dispatch Semaphore
發送一個信號告訴它對象能夠進行操做,而後開始進行下一操做,此時Dispatch Semaphore
會再次屏蔽其餘操做,直到收到對象操做完成的信號。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
建立一個dispatch_semaphore_t
類型的對象semaphore
,這個就至關於上面所說的信號,它的參數用來判斷是否須要等待。也就是上面所得是否屏蔽其餘操做,當參數爲0時,是等待。參數爲1或者大於1時,是不等待。當參數爲0時會一直等待直到收到信號,收到一次信號semaphore
會自加1,這樣semaphore
大於或者等於1,因此等待取消會執行下面的操做。
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
用來執行等待操做,當時開始等待阻止其餘將要進行的操做。直到接受到信號,由於接受信號自加1因此取消等待。
dispatch_semaphore_waitsemaphore = 0dispatch_semaphore_waitsemaphoredispatch_semaphore_wait
注意
當執行dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
這行代碼時,若是semaphore
大於或者等於1,這行代碼會自動將semaphore
減去1。每運行一次semaphore - 1
直到semaphore
爲0。
dispatch_semaphore_signal(semaphore);
會讓semaphore進行加1的操做。
dispatch_semaphore_signal
Dispatch Semaphore
代碼示例
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); NSMutableArray *array = [NSMutableArray array]; /** 建立Dispatch Semaphore 而且設置semaphore爲1 */ dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); for (unsigned int i = 0; i<100; i++) { dispatch_async(queue, ^{ /** 由於semaphore爲1因此dispatch_semaphore_wait不等待執行下面的操做 semaphore自減1此時semaphore==0 下一次操做將會等待,一直等到semaphore >= 1 */ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); [array addObject:[[NSObject alloc] init]]; /** 上面添加數據執行完後dispatch_semaphore_signal操做會使semaphore加1 此時其餘線程中的dispatch_semaphore_wait由於semaphore = 1 因此取消等待執行下面操做 */ dispatch_semaphore_signal(semaphore); }); }
若是在應用中一段代碼只想讓它執行一次,那麼就須要用到dispatch_once
,通常用於建立單例。
static NSObject *object = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ object=[[NSObject alloc] init]; });