GCD 系列知識總結(jié)(NSOperation比較)

1. 創(chuàng)建隊列

//自己創(chuàng)建串行隊列

dispatch_queue_t dySerial = dispatch_queue_create("串行隊列", DISPATCH_QUEUE_SERIAL);

//自己創(chuàng)建并行隊列

dispatch_queue_t dyConcurrent = dispatch_queue_create("并行隊列", DISPATCH_QUEUE_CONCURRENT);

//系統(tǒng)默認(rèn)主隊列诲祸,就是主線程

dispatch_queue_t osMainSerial = dispatch_get_main_queue();

//系統(tǒng)默認(rèn)的全局并行隊列

dispatch_queue_t osConcurrent = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

2. 執(zhí)行列隊

//異步執(zhí)行? ? ? dispatch_async("傳入隊列", 任務(wù)block);

//同步執(zhí)行? ? ? ?dispatch_sync("傳入隊列", 任務(wù)block);

3. 不同執(zhí)行 + 不同隊列效果

3.1 同步執(zhí)行 + 并行隊列 不會創(chuàng)建多線程 順序執(zhí)行

3.2 異步執(zhí)行 + 并發(fā)隊列 會創(chuàng)建多個線程

3.3 同步執(zhí)行 + 串行隊列 不會創(chuàng)建新線程

3.4 異步執(zhí)行 + 串行隊列 只創(chuàng)建了一個線程(區(qū)別于當(dāng)前線程的新線程)

3.5 同步/異步 + 主隊列dispatch_get_main_queue()執(zhí)行效果

3.5.1? 特別注意: 同步執(zhí)行 + 對于特殊的串行隊列 dispatch_get_main_queue() 會出現(xiàn)死鎖

首先執(zhí)行任務(wù)1慌申,然后遇到dispatch_sync 同步線程出刷,當(dāng)前線程進入等待,等待同步線程中的任務(wù)2執(zhí)行完再執(zhí)行任務(wù)3皆的,這個任務(wù)2是加入到mainQueue主隊列中。

dispatch_sync(osMainSerial,^(void)()) 是同步一個任務(wù)到主隊列中蹋盆,而當(dāng)前去同步的正是主隊列费薄。因為隊列的執(zhí)行是FIFO(先進先出),所以兩個都在等待對方完成栖雾,就會造成死鎖楞抡。

3.5.2 如果這個操作放在非主隊列中執(zhí)行就不會有問題

3.6 異步執(zhí)行 + 對于特殊的串行隊列 dispatch_get_main_queue() 異步執(zhí)行的時候 不會創(chuàng)建新線程,只會使用當(dāng)前線程

4. 關(guān)于死鎖

4.1 在上3.5.1上介紹 同步執(zhí)行(dispatch_sync) + 主隊列 = 死鎖析藕。那么如果同步執(zhí)行的 + 非主隊列 是否就一定不會出現(xiàn)死鎖了召廷?看下面的代碼:

會發(fā)現(xiàn)還是會出現(xiàn)死鎖的情況,綜上所述不難發(fā)現(xiàn)這種死鎖產(chǎn)生的原因:

在串行隊列A(主隊列或自己創(chuàng)建的串行隊列)的線程中同步執(zhí)行一個任務(wù)噪径,而且這個任務(wù)還是放在串行隊列A中柱恤。這樣就會出現(xiàn)死鎖。

4.2 還有一種死鎖找爱,就是當(dāng)前的串行隊列被阻塞梗顺,然后同步任務(wù)到這個隊列就會出現(xiàn)

執(zhí)行順序:

main隊列中死循環(huán)不結(jié)束,任務(wù)3不執(zhí)行车摄,任務(wù)3又是global全局隊列同步過去的寺谤,global中同步的任務(wù)3不執(zhí)行完、任務(wù)4是不會執(zhí)行的吮播。

了解死鎖的發(fā)生時機后就很容易理解变屁,所謂隊列(串行并行)就是一個順序執(zhí)行的甬道舆乔。所謂同步異步就是決定執(zhí)行這個隊列的時候是否開辟新的線程去執(zhí)行来累。

關(guān)于GCD之間的通信沸停,就是一個概念記住就好当窗。不同的隊列直接相互通信这吻,只需要在執(zhí)行任務(wù)的時候同步或異步執(zhí)行任務(wù)的時候調(diào)用別的隊列傳值

5.? GCD常用方法

5.1 組操作dispatch_group

5.1.1? dispatch_group_notify宵膨、dispatch_group_wait 基本用法

異步執(zhí)行多個耗時任務(wù)运杭,然后多個任務(wù)都執(zhí)行完畢后再回到主線程執(zhí)行任務(wù)蛹头。這時候我們可以用到 GCD 的隊列組

dispatch_group_notify和dispatch_group_wait意思就是notify有block你可自己干某事,一個沒block 干某事只能接著wait后面寫順序執(zhí)行遮晚。

5.1.2? dispatch_group_enter性昭、dispatch_group_leave 基本用法

先看一個例子

發(fā)現(xiàn)doSomeThing1還沒執(zhí)行完就進入dispatch_group_notify打印刷新頁面,這是為什么了县遣?看代碼會發(fā)現(xiàn)doSomeThing1是放在了一個異步線程中執(zhí)行的糜颠。但是dispatch_group_notify只會監(jiān)聽dispatch_group_async中執(zhí)行的同步任務(wù)。如果dispatch_group_async執(zhí)行的是個異步任務(wù)萧求,那notify 監(jiān)聽結(jié)果可能不是你想要的其兴。

那我們能不能實現(xiàn)dispatch_group_async執(zhí)行多異步任務(wù),同時還能監(jiān)聽到任務(wù)全部完成了饭聚?做到這些就需要dispatch_group_enter和dispatch_group_leave了忌警,看下面例子:

dispatch_group_enter 用來標(biāo)記這個任務(wù)的開始,dispatch_group_leave用來標(biāo)記任務(wù)完成秒梳。這樣成對標(biāo)記可以實現(xiàn)標(biāo)記dispatch_group_async任務(wù)的真正執(zhí)行完畢,dispatch_group_notify也就能監(jiān)聽到了法绵。

記住:dispatch_group_enter 和 dispatch_group_leave 是成對出現(xiàn)的酪碘,不然dispatch_group 隊列會出錯朋譬。

5.1.3 用dispatch_group 處理多網(wǎng)絡(luò)請求都返回在刷新頁面

根據(jù)5.1.2 我們可以在實際開發(fā)中運用dispatch_group 來處理多個網(wǎng)絡(luò)請求都完成的通知。因為我們的網(wǎng)絡(luò)請求都是異步的兴垦,正好可以用dispatch_group_enter 和 dispatch_group_leave實現(xiàn)標(biāo)記完成

5.2 只執(zhí)行一次 dispatch_once

創(chuàng)建單例或者有整個程序運行過程中只執(zhí)行一次的代碼時使用徙赢。保證某段代碼在程序運行過程中只被執(zhí)行1次,并且即使在多線程的環(huán)境下探越,dispatch_once也可以保證線程安全狡赐。

5.3 延時執(zhí)行方法:dispatch_after

在指定時間(例如3秒)之后執(zhí)行某個任務(wù)。dispatch_after函數(shù)并不是在指定時間之后才開始執(zhí)行處理钦幔,而是在指定時間之后將任務(wù)追加到主隊列中枕屉。嚴(yán)格來說,這個時間并不是絕對準(zhǔn)確的鲤氢,但想要大致延遲執(zhí)行任務(wù)搀擂,dispatch_after函數(shù)是很有效的

5.4 柵欄方法 dispatch_barrier_async

我們有時需要異步執(zhí)行兩組操作,而且第一組操作執(zhí)行完之后卷玉,才能開始執(zhí)行第二組操作哨颂。這樣我們就需要一個相當(dāng)于柵欄一樣的一個方法將兩組異步執(zhí)行的操作組給分割起來,當(dāng)然這里的操作組里可以包含一個或多個任務(wù)相种。這就需要用到dispatch_barrier_async方法在兩個操作組間形成柵欄威恼。

發(fā)現(xiàn):任務(wù)1和任務(wù)2 全部執(zhí)行完畢后 再執(zhí)行追加任務(wù),然后再執(zhí)行任務(wù)3和任務(wù)4,通過這個例子我們能明白dispatch_barrier_async主要是分割異步任務(wù)的作用沃测。

5.5? dispatch_apply 快速遍歷

和一般我們用for循環(huán)遍歷不同的是缭黔,如果dispatch_apply調(diào)用的時候傳入queue的是并發(fā)隊列食茎,那么它的遍歷就是異步執(zhí)行的蒂破,會在多個線程中進行,而我們直接for循環(huán)都是在當(dāng)前線程中進行的别渔。如下

發(fā)現(xiàn)打印的線程號不是按順序的附迷,但是如果傳入的是個串行隊列(非主隊列)它就和普通的for循環(huán)沒什么不同了。如下

發(fā)現(xiàn)輸出是按照順序的哎媚,和for循環(huán)沒什么不同喇伯,都是在主線程中進行的。

注意:如果執(zhí)行dispatch_apply的線程是串行隊列A的線程拨与,同時執(zhí)行dispatch_apply時傳入queue的也是串行隊列A稻据,會發(fā)生死鎖。比如直接在主隊列中執(zhí)行dispatch_apply买喧,同時傳入queue也是主隊列捻悯,如:

當(dāng)然這樣做也沒什么意義,一般dispatch_apply都是為了高效的異步的遍歷數(shù)組淤毛。所以一般dispatch_apply傳入的queue都是一個并發(fā)隊列今缚,同時還把dispatch_apply整體放入一個異步執(zhí)行的并發(fā)隊列中,如:

5.6 信號量 dispatch_semaphore

使用dispatch_semaphore 可以更好的控制并發(fā)多線程的任務(wù)處理低淡。

5.6.1 控制并發(fā)線程數(shù)量

現(xiàn)在有一組圖片地址姓言,需要開辟一個并發(fā)隊列來下載這些圖片,需要等這些圖片都下載完了才去做下面的事情

直接運行會發(fā)現(xiàn)會一下開辟imageArray.count個線程來處理任務(wù)蔗蹋。假如這里imageArray.count是100多何荚,很顯然,這樣的暴力處理是不切合實際的猪杭。使用Semaphore我們可以控制同時并發(fā)的線程數(shù)量餐塘。效果如下:

發(fā)現(xiàn)我們先開辟5個線程去下載,5個都下載完成了后再開辟5個繼續(xù)下載胁孙,這樣就可以減輕系統(tǒng)并發(fā)的數(shù)量唠倦。

看結(jié)果你是不是感覺這有點像dispatch_barrier_async都是把任務(wù)拆成幾部分一塊一塊執(zhí)行,但是他們是有本質(zhì)不同的:dispatch_barrier_async控制的是任務(wù)涮较,它操作的對象是任務(wù)分塊稠鼻,不會處理線程的多少,而Semaphore控制同時并發(fā)的線程數(shù)量狂票,再由一定的線程數(shù)量去執(zhí)行任務(wù)候齿。這好比同樣是蓋樓要10個人,barrier類似于我把樓分成2部分,我們10個人先蓋第一部分慌盯,第一部分完了再蓋第二部分周霉。而Semaphore是我們同時只讓5個人蓋樓,剩下的休息亚皂,誰干完了誰休息俱箱,之前休息的接著上來干。

那如果dispatch_semaphore_create(5)改成dispatch_semaphore_create(1)會發(fā)現(xiàn)雖然調(diào)用的是并發(fā)隊列灭必,但是限制信號量總數(shù)為1 后狞谱,并發(fā)隊列就變成了串行隊列

5.6.2 對多線程并發(fā)執(zhí)行任務(wù)共享資源進行? 加鎖處理

案例:總共有20張火車票,有兩個售賣火車票的窗口禁漓,一個是北京火車票售賣窗口跟衅,另一個是上海火車票售賣窗口播歼。兩個窗口同時售賣火車票伶跷,賣完為止。

這里的問題關(guān)鍵是秘狞,兩個窗口同時賣票叭莫,在賣出一張票后總票數(shù)都會減1,如何確保票不多賣或賣叉谒撼?這就相當(dāng)于計算機中的多條線程同時寫一個資源食寡,可能會出現(xiàn)混亂的問題

觀察發(fā)現(xiàn)余票在 11、6的時候出現(xiàn)混亂廓潜,多賣了票抵皱。這就是多線程訪問公共資源出現(xiàn)不同步的問題。我們使用dispatch_semaphore 來加鎖處理一下辩蛋,看看結(jié)果怎么樣呻畸?

發(fā)現(xiàn)總剩余票數(shù)沒有發(fā)生錯位。其實dispatch_semaphore這里就是控制了多線程處理任務(wù) 并發(fā)時機悼院,也是5.1.1的一種特殊形式伤为。

5.6.3 頁面有多個網(wǎng)絡(luò)請求都完成后才處理任務(wù)

上面說的使用dispatch_group_enter和dispatch_group_leave可以實現(xiàn)多網(wǎng)絡(luò)請求完成監(jiān)聽處理。我們使用dispatch_semaphore也可以完成這種操作

補充: 那么如何讓這些網(wǎng)絡(luò)請求有個先后順序了据途,就是說請求1返回回來的參數(shù)我要用到請求2上绞愚,這樣的需求也很常見,用到的知識就不是GCD的了颖医,而是NSOperation了

其中的[self request]里面代碼還是使用信號量來控制網(wǎng)絡(luò)請求真正結(jié)束

對于NSOperation實現(xiàn)多線程的方式有兩種:

1.將要執(zhí)行的任務(wù)封裝到一個 NSOperation 對象中

2.將要執(zhí)行的任務(wù)添加到一個 NSOperationQueue 對象中

NSOperation 只是一個抽象類位衩,所以不能封裝任務(wù)。但它有 2 個子類用于封裝任務(wù)熔萧。分別是:NSInvocationOperation 和 NSBlockOperation 糖驴。創(chuàng)建一個 Operation 后僚祷,需要調(diào)用 start 方法來啟動任務(wù),它會默認(rèn)在當(dāng)前隊列同步執(zhí)行贮缕。你也可以調(diào)用 cancel 方法在中途取消一個任務(wù)辙谜。

總結(jié):NSOperation比GCD的好處

1、NSOperation能通過KVO鍵值觀察者來實時監(jiān)控operation的狀態(tài)(是否執(zhí)行感昼,是否取消)装哆, 而GCD無法通過KVO來實時監(jiān)控。通過completBlock來回調(diào)已經(jīng)執(zhí)行完畢抑诸。

2烂琴、NSOperation能通過設(shè)置依賴,使任務(wù)之間有先后順序蜕乡。GCD則可以通過柵欄函數(shù)來實現(xiàn)。

3梗夸、NSOperation能設(shè)置并行隊列中任務(wù)的優(yōu)先級(優(yōu)先級的作用是讓優(yōu)先級高的線程需要cpu時能夠更大幾率優(yōu)先得到cpu)反症,GCD是設(shè)置不同任務(wù)隊列(queue)的優(yōu)先級辛块,要實現(xiàn)block的優(yōu)先級,需要很多代碼铅碍。

4润绵、NSOperation是將任務(wù)放入隊列中來執(zhí)行的。GCD是將任務(wù)放入隊列中胞谈,通過同步異步函數(shù)來執(zhí)行(可以多個同步異步函數(shù))尘盼。

5、總體來說GCD更簡潔

補充:

1烦绳、怎么取消正在執(zhí)行的gcd任務(wù):

可以仿照operation的工作原理卿捎,設(shè)置一個BOOL變量,當(dāng)需要停止時設(shè)置成YES,執(zhí)行任務(wù)的時候去判斷這個狀態(tài)

2径密、多線程使用帶來的問題:資源競爭午阵、優(yōu)先倒置、死鎖


以上是根據(jù)別人的博客和自己代碼實踐所得享扔,如有冒犯和不足歡迎大家批評底桂。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市惧眠,隨后出現(xiàn)的幾起案子籽懦,更是在濱河造成了極大的恐慌,老刑警劉巖锉试,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猫十,死亡現(xiàn)場離奇詭異览濒,居然都是意外死亡,警方通過查閱死者的電腦和手機拖云,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門贷笛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人宙项,你說我怎么就攤上這事乏苦。” “怎么了尤筐?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵汇荐,是天一觀的道長。 經(jīng)常有香客問我盆繁,道長掀淘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任油昂,我火速辦了婚禮革娄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘冕碟。我一直安慰自己拦惋,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布安寺。 她就那樣靜靜地躺著厕妖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪挑庶。 梳的紋絲不亂的頭發(fā)上言秸,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機與錄音挠羔,去河邊找鬼井仰。 笑死,一個胖子當(dāng)著我的面吹牛破加,可吹牛的內(nèi)容都是我干的俱恶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼范舀,長吁一口氣:“原來是場噩夢啊……” “哼合是!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起锭环,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤聪全,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后辅辩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體难礼,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡娃圆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蛾茉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片讼呢。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖谦炬,靈堂內(nèi)的尸體忽然破棺而出悦屏,到底是詐尸還是另有隱情,我是刑警寧澤键思,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布础爬,位于F島的核電站,受9級特大地震影響吼鳞,放射性物質(zhì)發(fā)生泄漏看蚜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一赖条、第九天 我趴在偏房一處隱蔽的房頂上張望失乾。 院中可真熱鬧,春花似錦纬乍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至墓贿,卻和暖如春茧泪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背聋袋。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工队伟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人幽勒。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓嗜侮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親啥容。 傳聞我的和親對象是個殘疾皇子锈颗,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容