GCD是多線程編程中很常用的技術,同時,作為一項重要的知識點,在面試中也是“常來之客”,本文通過API進行GCD的歸納和總結
GCD之前
在蘋果引入GCD技術之前,Cocoa框架中的NSObject類提供了performSelectorInBackground:withObject
和performSelectorOnMainThread
等來實現(xiàn)多線程編程,此外還有NSThread、NSOperation等,伴隨GCD的誕生,NSOperation也進行重寫,基于GCD實現(xiàn)
關于多線程編程
一個CPU一次只能執(zhí)行一個命令,這樣連續(xù)執(zhí)行命令,相當于一條無分叉的路徑,這樣的路徑就是“線程”。OS和iOS的核心XNU內(nèi)核在進行操作系統(tǒng)事件處理的時候,會切換執(zhí)行路徑。每條執(zhí)行路徑的狀態(tài),會保存到每條路徑專用的內(nèi)存塊中,便于下次執(zhí)行時復原信息。這種來回切換的操作可以被稱為“上下文切換”,也正是這種切換產(chǎn)生了多線程。
多線程需要注意的問題
- 數(shù)據(jù)不一致:例如,兩個線程同時更新一個數(shù)據(jù)
- 死鎖:兩個線程互相等待對方執(zhí)行結束
- 內(nèi)存消耗:線程過多會消耗大量內(nèi)存
...
GCD的API
dispatch_queue_t
分為兩種
-
Serial Dispatch Queue
:串行隊列-
系統(tǒng)提供的:
dispatch_get_main_queue
-
自定義創(chuàng)建:
dispatch_queue_create("name", DISPATCH_QUEUE_SERIAL)
其中的name是該queue的名字
-
系統(tǒng)提供的:
-
Concurrent Dispatch Queue
:并行隊列-
系統(tǒng)提供的:
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)
其中第一個參數(shù)有四種,對應不同優(yōu)先級DISPATCH_QUEUE_PRIORITY_BACKGROUND
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_DEFAULT
DISPATCH_QUEUE_PRIORITY_HIGH
-
自定義創(chuàng)建:
dispatch_queue_create("name",DISPATCH_QUEUE_CONCURRENT)
-
系統(tǒng)提供的:
dispatch_set_target_queue
-
dispatch_queue_create
生成的dispatch_queue_t
的優(yōu)先級都是默認優(yōu)先級,如果想要改變優(yōu)先級可以使用:
dispatch_queue_t queue1 = dispatch_queue_create("name", DISPATCH_QUQUE_SERIAL);
dispatch_queue_t queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND);
dispatch_set_target_queue(queue1, queue2);
- 同時如果將多個串行隊列的目標制定為同一個串行隊列,那么本應該并行執(zhí)行的各個串行隊列只能串行地進行執(zhí)行
dispatch_after
在指定時間之后執(zhí)行處理,本質(zhì)上是在指定時間之后將任務追加到隊列中
dispatch_group_t
隊列組可以用來實現(xiàn):當所有處理結束之后,執(zhí)行一個操作。正常來說使用串行隊列是很容易實現(xiàn)的,但是如果是并行隊列的話,想要實現(xiàn)這個操作,就需要借助隊列組來實現(xiàn)
dispatch_group_notify
- 用于group中所有任務執(zhí)行完之后追加一個操作執(zhí)行
- 該方法不會阻塞當前線程
dispatch_group_wait
long result = dispatch_group_wait(group, time);
- 該函數(shù)會阻塞當前線程(即代碼所處的線程)
- time的選擇
- 可以是一個固定的時間,如果超時了,該函數(shù)就會返回
- 也可以是DISPATCH_TIME_FOREVER,表示一直等待,直到group中所有操作執(zhí)行完畢,函數(shù)才返回。
- 返回值
- 0:代表group中的任務全部執(zhí)行完了
- 非0:代表超時了
dispatch_barrier_async
一般來說,關于數(shù)據(jù)庫的操作,使用串行隊列能夠避免數(shù)據(jù)異常的問題。但是實際上,讀取操作之間是沒有影響的,為了提高效率,讀取操作通過是并行的,只需要保證寫操作的時候,沒有任何一個讀取操作在進行即可。
dispatch_async(queue, block_reading1);
dispatch_async(queue, block_reading2);
dispatch_barrier_async(queue, block_writing);
dispatch_async(queue, block_reading3);
dispatch_async(queue, block_reading4);
以上代碼中的寫操作,會等待上面代碼中的讀操作(已經(jīng)被添加到隊列中的任務)執(zhí)行完畢之后,開始執(zhí)行,此時,寫操作以下的代碼中的讀操作需要的等待寫操作執(zhí)行完之后才能添加到隊列中執(zhí)行
dispatch_async和dispatch_sync
-
dispatch_async
是異步執(zhí)行,代表不會阻塞當前線程,會另外開辟線程執(zhí)行任務 -
dispatch_sync
是同步執(zhí)行,代表會阻塞當前線程,會在當前線程執(zhí)行任務
dispatch_apply
該函數(shù)可以將block中的任務指定次數(shù)加入到隊列中,并且會阻塞當前線程,直到所有的任務全部執(zhí)行完
dispatch_suspend和dispatch_resume
-
dispatch_suspend
:掛起后,添加到隊列中的任務,尚未執(zhí)行的處理都會停止執(zhí)行 -
dispatch_resume
:將會恢復以上操作
dispatch_semaphore_t
GCD中的信號量,用于控制并行執(zhí)行,在GCD中,控制并行可以通串行隊列和dispatch_barrier_async來做,而信號量semaphore可以更加精確地控制并行
-
dispatch_semaphore_create(number )
:number為信號量的初始量 -
dispatch_semaphore_wait(semaphore, time)
- 其中time和group中是一樣的
- 返回值:
- 0:如果信號量大于等于1,返回0,同時將信息號量減去1
- 非0:超過指定時間(同時這時信號量為0)
-
dispatch_semaphore_signal(semaphore)
:將信號量+1
dispatch_once
用于單例處理,相關代碼只會執(zhí)行一次(即使在多核情況下,也是安全的)