GCD編程的核心就是dispatch隊列陨囊,dispatch block的執(zhí)行最終都會放進(jìn)某個隊列中去進(jìn)行逗抑,它類似NSOperationQueue但更復(fù)雜也更強(qiáng)大剧辐,并且可以嵌套使用。所以說邮府,結(jié)合block實(shí)現(xiàn)的GCD荧关,把函數(shù)閉包(Closure)的特性發(fā)揮得淋漓盡致。
dispatch隊列的生成可以有這幾種方式:
1. dispatch_queue_tqueue =dispatch_queue_create("com.dispatch.serial",DISPATCH_QUEUE_SERIAL);//生成一個串行隊列褂傀,隊列中的block按照先進(jìn)先出(FIFO)的順序去執(zhí)行忍啤,實(shí)際上為單線程執(zhí)行。第一個參數(shù)是隊列的名稱仙辟,在調(diào)試程序時會非常有用檀轨,所有盡量不要重名了。
2. dispatch_queue_tqueue =dispatch_queue_create("com.dispatch.concurrent",DISPATCH_QUEUE_CONCURRENT);//生成一個并發(fā)執(zhí)行隊列欺嗤,block被分發(fā)到多個線程去執(zhí)行
3.dispatch_queue_tqueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//獲得程序進(jìn)程缺省產(chǎn)生的并發(fā)隊列参萄,可設(shè)定優(yōu)先級來選擇高、中煎饼、低三個優(yōu)先級隊列讹挎。由于是系統(tǒng)默認(rèn)生成的,所以無法調(diào)用dispatch_resume()和dispatch_suspend()來控制執(zhí)行繼續(xù)或中斷吆玖。需要注意的是筒溃,三個隊列不代表三個線程,可能會有更多的線程沾乘。并發(fā)隊列可以根據(jù)實(shí)際情況來自動產(chǎn)生合理的線程數(shù)怜奖,也可理解為dispatch隊列實(shí)現(xiàn)了一個線程池的管理,對于程序邏輯是透明的翅阵。
官網(wǎng)文檔解釋說共有三個并發(fā)隊列歪玲,但實(shí)際還有一個更低優(yōu)先級的隊列,設(shè)置優(yōu)先級為DISPATCH_QUEUE_PRIORITY_BACKGROUND掷匠。Xcode調(diào)試時可以觀察到正在使用的各個dispatch隊列滥崩。
4.dispatch_queue_tqueue =dispatch_get_main_queue();//獲得主線程的dispatch隊列,實(shí)際是一個串行隊列讹语。同樣無法控制主線程dispatch隊列的執(zhí)行繼續(xù)或中斷钙皮。
接下來我們可以使用dispatch_async或dispatch_sync函數(shù)來加載需要運(yùn)行的block。
dispatch_async(queue, ^{
//block具體代碼
});//異步執(zhí)行block,函數(shù)立即返回
dispatch_sync(queue, ^{
//block具體代碼
});//同步執(zhí)行block短条,函數(shù)不返回导匣,一直等到block執(zhí)行完畢。編譯器會根據(jù)實(shí)際情況優(yōu)化代碼茸时,所以有時候你會發(fā)現(xiàn)block其實(shí)還在當(dāng)前線程上執(zhí)行逐抑,并沒用產(chǎn)生新線程。
實(shí)際編程經(jīng)驗(yàn)告訴我們屹蚊,盡可能避免使用dispatch_sync,嵌套使用時還容易引起程序死鎖进每。
如果queue1是一個串行隊列的話汹粤,這段代碼立即產(chǎn)生死鎖:
dispatch_sync(queue1, ^{
dispatch_sync(queue1, ^{
......
});
......
});
不妨思考下,為什么下面代碼也肯定死鎖:
dispatch_sync(dispatch_get_main_queue(),^{
......
});
那實(shí)際運(yùn)用中田晚,一般可以用dispatch這樣來寫嘱兼,常見的網(wǎng)絡(luò)請求數(shù)據(jù)多線程執(zhí)行模型:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//子線程中開始網(wǎng)絡(luò)請求數(shù)據(jù)
//更新數(shù)據(jù)模型
dispatch_sync(dispatch_get_main_queue(), ^{
//在主線程中更新UI代碼
});
});
程序的后臺運(yùn)行和UI更新代碼緊湊,代碼邏輯一目了然贤徒。
dispatch隊列是線程安全的芹壕,可以利用串行隊列實(shí)現(xiàn)鎖的功能。比如多線程寫同一數(shù)據(jù)庫接奈,需要保持寫入的順序和每次寫入的完整性踢涌,簡單地利用串行隊列即可實(shí)現(xiàn):
dispatch_queue_tqueue1 =dispatch_queue_create("com.dispatch.writedb",DISPATCH_QUEUE_SERIAL);
- (void)writeDB:(NSData *)data
{
dispatch_async(queue1, ^{
//write database
});
}
下一次調(diào)用writeDB:必須等到上次調(diào)用完成后才能進(jìn)行,保證writeDB:方法是線程安全的序宦。
dispatch隊列還實(shí)現(xiàn)其它一些常用函數(shù)睁壁,包括:
void?dispatch_apply(size_titerations,dispatch_queue_tqueue,void(^block)(size_t));//重復(fù)執(zhí)行block,需要注意的是這個方法是同步返回互捌,也就是說等到所有block執(zhí)行完畢才返回潘明,如需異步返回則嵌套在dispatch_async中來使用。多個block的運(yùn)行是否并發(fā)或串行執(zhí)行也依賴queue的是否并發(fā)或串行秕噪。
void?dispatch_barrier_async(dispatch_queue_tqueue,dispatch_block_tblock);//這個函數(shù)可以設(shè)置同步執(zhí)行的block钳降,它會等到在它加入隊列之前的block執(zhí)行完畢后,才開始執(zhí)行腌巾。在它之后加入隊列的block遂填,則等到這個block執(zhí)行完畢后才開始執(zhí)行。
void?dispatch_barrier_sync(dispatch_queue_tqueue,dispatch_block_tblock);//同上澈蝙,除了它是同步返回函數(shù)
void dispatch_after(dispatch_time_twhen,?dispatch_queue_tqueue,dispatch_block_tblock);//延遲執(zhí)行block
最后再來看看dispatch隊列的一個很有特色的函數(shù):
void?dispatch_set_target_queue(dispatch_object_tobject,dispatch_queue_tqueue);
它會把需要執(zhí)行的任務(wù)對象指定到不同的隊列中去處理城菊,這個任務(wù)對象可以是dispatch隊列,也可以是dispatch源(以后博文會介紹)碉克。而且這個過程可以是動態(tài)的凌唬,可以實(shí)現(xiàn)隊列的動態(tài)調(diào)度管理等等。比如說有兩個隊列dispatchA和dispatchB,這時把dispatchA指派到dispatchB:
dispatch_set_target_queue(dispatchA, dispatchB);
那么dispatchA上還未運(yùn)行的block會在dispatchB上運(yùn)行客税。這時如果暫停dispatchA運(yùn)行:
dispatch_suspend(dispatchA);
則只會暫停dispatchA上原來的block的執(zhí)行况褪,dispatchB的block則不受影響。而如果暫停dispatchB的運(yùn)行更耻,則會暫停dispatchA的運(yùn)行测垛。
這里只簡單舉個例子,說明dispatch隊列運(yùn)行的靈活性秧均,在實(shí)際應(yīng)用中你會逐步發(fā)掘出它的潛力食侮。
dispatch隊列不支持cancel(取消),沒有實(shí)現(xiàn)dispatch_cancel()函數(shù)目胡,不像NSOperationQueue锯七,不得不說這是個小小的缺憾。