qileilove

          blog已經(jīng)轉(zhuǎn)移至github,大家請(qǐng)?jiān)L問(wèn) http://qaseven.github.io/

          iOS開發(fā)之GCD使用總結(jié)

            GCD是iOS的一種底層多線程機(jī)制,今天總結(jié)一下GCD的常用API和概念,希望對(duì)大家的學(xué)習(xí)起到幫助作用。
            GCD隊(duì)列的概念
            在多線程開發(fā)當(dāng)中,程序員只要將想做的事情定義好,并追加到DispatchQueue(派發(fā)隊(duì)列)當(dāng)中就好了。
            派發(fā)隊(duì)列分為兩種,一種是串行隊(duì)列(SerialDispatchQueue),一種是并行隊(duì)列(ConcurrentDispatchQueue)。
            一個(gè)任務(wù)就是一個(gè)block,比如,將任務(wù)添加到隊(duì)列中的代碼是:
            1 dispatch_async(queue, block);
            當(dāng)給queue添加多個(gè)任務(wù)時(shí),如果queue是串行隊(duì)列,則它們按順序一個(gè)個(gè)執(zhí)行,同時(shí)處理的任務(wù)只有一個(gè)。
            當(dāng)queue是并行隊(duì)列時(shí),不論第一個(gè)任務(wù)是否結(jié)束,都會(huì)立刻開始執(zhí)行后面的任務(wù),也就是可以同時(shí)執(zhí)行多個(gè)任務(wù)。
            但是并行執(zhí)行的任務(wù)數(shù)量取決于XNU內(nèi)核,是不可控的。比如,如果同時(shí)執(zhí)行10個(gè)任務(wù),那么10個(gè)任務(wù)并不是開啟10個(gè)線程,線程會(huì)根據(jù)任務(wù)執(zhí)行情況復(fù)用,由系統(tǒng)控制。
            獲取隊(duì)列
            系統(tǒng)提供了兩個(gè)隊(duì)列,一個(gè)是MainDispatchQueue,一個(gè)是GlobalDispatchQueue。
            前者會(huì)將任務(wù)插入主線程的RunLoop當(dāng)中去執(zhí)行,所以顯然是個(gè)串行隊(duì)列,我們可以使用它來(lái)更新UI。
            后者則是一個(gè)全局的并行隊(duì)列,有高、默認(rèn)、低和后臺(tái)4個(gè)優(yōu)先級(jí)。
            它們的獲取方式如下:
            1 dispatch_queue_t queue = dispatch_get_main_queue();
            2
            3 dispatch queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRORITY_DEFAULT, 0)
            執(zhí)行異步任務(wù)
            1     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
            2     dispatch_async(queue, ^{
            3         //...
            4     });
            這個(gè)代碼片段直接在子線程里執(zhí)行了一個(gè)任務(wù)塊。使用GCD方式任務(wù)是立即開始執(zhí)行的
            它不像操作隊(duì)列那樣可以手動(dòng)啟動(dòng),同樣,缺點(diǎn)也是它的不可控性。
            令任務(wù)只執(zhí)行一次
            1 + (id)shareInstance {
            2     static dispatch_once_t onceToken;
            3     dispatch_once(&onceToken, ^{
            4         _shareInstance = [[self alloc] init];
            5     });
            6 }
            這種只執(zhí)行一次且線程安全的方式經(jīng)常出現(xiàn)在單例構(gòu)造器當(dāng)中。
            任務(wù)組
            有時(shí)候,我們希望多個(gè)任務(wù)同時(shí)(在多個(gè)線程里)執(zhí)行,再他們都完成之后,再執(zhí)行其他的任務(wù),
            于是可以建立一個(gè)分組,讓多個(gè)任務(wù)形成一個(gè)組,下面的代碼在組中多個(gè)任務(wù)都執(zhí)行完畢之后再執(zhí)行后續(xù)的任務(wù):
            1     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
            2     dispatch_group_t group = dispatch_group_create();
            3
            4     dispatch_group_async(group, queue, ^{ NSLog(@"1"); });
            5     dispatch_group_async(group, queue, ^{ NSLog(@"2"); });
            6     dispatch_group_async(group, queue, ^{ NSLog(@"3"); });
            7     dispatch_group_async(group, queue, ^{ NSLog(@"4"); });
            8     dispatch_group_async(group, queue, ^{ NSLog(@"5"); });
            9
            10     dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"done"); });延遲執(zhí)行任務(wù)
            1     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            2         //...
            3     });
            這段代碼將會(huì)在10秒后將任務(wù)插入RunLoop當(dāng)中。
            dispatch_asycn和dispatch_sync
            先前已經(jīng)有過(guò)一個(gè)使用dispatch_async執(zhí)行異步任務(wù)的一個(gè)例子,下面來(lái)看一段代碼:
            1     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
            2
            3     dispatch_async(queue, ^{
            4         NSLog(@"1");
            5     });
            6
            7     NSLog(@"2");
            這段代碼首先獲取了全局隊(duì)列,也就是說(shuō),dispatch_async當(dāng)中的任務(wù)被丟到了另一個(gè)線程里去執(zhí)行,async在這里的含義是,當(dāng)當(dāng)前線程給子線程分配了block當(dāng)中的任務(wù)之后,當(dāng)前線程會(huì)立即執(zhí)行,并不會(huì)發(fā)生阻塞,也就是異步的。那么,輸出結(jié)果不是12就是21,因?yàn)槲覀儧]法把控兩個(gè)線程RunLoop里到底是怎么執(zhí)行的。
            類似的,還有一個(gè)“同步”方法dispatch_sync,代碼如下:
            1     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
            2
            3     dispatch_sync(queue, ^{
            4         NSLog(@"1");
            5     });
            6
            7     NSLog(@"2");
            這就意味著,當(dāng)主線程將任務(wù)分給子線程后,主線程會(huì)等待子線程執(zhí)行完畢,再繼續(xù)執(zhí)行自身的內(nèi)容,那么結(jié)果顯然就是12了。
            需要注意的一點(diǎn)是,這里用的是全局隊(duì)列,那如果把dispatch_sync的隊(duì)列換成主線程隊(duì)列會(huì)怎么樣呢:
            1     dispatch_queue_t queue = dispatch_get_main_queue();
            2     dispatch_sync(queue, ^{
            3         NSLog(@"1");
            4     });
            這段代碼會(huì)發(fā)生死鎖,因?yàn)椋?/div>
            1.主線程通過(guò)dispatch_sync把block交給主隊(duì)列后,會(huì)等待block里的任務(wù)結(jié)束再往下走自身的任務(wù),
            2.而隊(duì)列是先進(jìn)先出的,block里的任務(wù)也在等待主隊(duì)列當(dāng)中排在它之前的任務(wù)都執(zhí)行完了再走自己。
            這種循環(huán)等待就形成了死鎖。所以在主線程當(dāng)中使用dispatch_sync將任務(wù)加到主隊(duì)列是不可取的。
            創(chuàng)建隊(duì)列
            我們可以使用系統(tǒng)提供的函數(shù)獲取主串行隊(duì)列和全局并行隊(duì)列,當(dāng)然也可以自己手動(dòng)創(chuàng)建串行和并行隊(duì)列,代碼為:
            1     dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.Steak.GCD", DISPATCH_QUEUE_SERIAL);
            2     dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("com.Steak.GCD", DISPATCH_QUEUE_CONCURRENT);
            在MRC下,手動(dòng)創(chuàng)建的隊(duì)列是需要釋放的
            1     dispatch_release(myConcurrentDispatchQueue);
            手動(dòng)創(chuàng)建的隊(duì)列和默認(rèn)優(yōu)先級(jí)全局隊(duì)列優(yōu)先級(jí)等同,如果需要修改隊(duì)列的優(yōu)先級(jí),需要:
            1     dispatch_queue_t myConcurrentDispatchQueue = dispatch_queue_create("com.Steak.GCD", DISPATCH_QUEUE_CONCURRENT);
            2     dispatch_queue_t targetQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
            3     dispatch_set_target_queue(myConcurrentDispatchQueue, targetQueue);
            上面的代碼修改隊(duì)列的優(yōu)先級(jí)為后臺(tái)級(jí)別,即與默認(rèn)的后臺(tái)優(yōu)先級(jí)的全局隊(duì)列等同。
            串行、并行隊(duì)列與讀寫安全性
            在向串行隊(duì)列(SerialDispatchQueue)當(dāng)中加入多個(gè)block任務(wù)后,一次只能同時(shí)執(zhí)行一個(gè)block,如果生成了n個(gè)串行隊(duì)列,并且向每個(gè)隊(duì)列當(dāng)中都添加了任務(wù),那么系統(tǒng)就會(huì)啟動(dòng)n個(gè)線程來(lái)同時(shí)執(zhí)行這些任務(wù)。
            對(duì)于串行隊(duì)列,正確的使用時(shí)機(jī),是在需要解決數(shù)據(jù)/文件競(jìng)爭(zhēng)問(wèn)題時(shí)使用它。比如,我們可以令多個(gè)任務(wù)同時(shí)訪問(wèn)一塊數(shù)據(jù),這樣會(huì)出現(xiàn)沖突,也可以把每個(gè)操作都加入到一個(gè)串行隊(duì)列當(dāng)中,因?yàn)榇嘘?duì)列一次只能執(zhí)行一個(gè)線程的任務(wù),所以不會(huì)出現(xiàn)沖突。
            但是考慮到串行隊(duì)列會(huì)因?yàn)樯舷挛那袚Q而拖慢系統(tǒng)性能,所以我們還是很期望采用并行隊(duì)列的,來(lái)看下面的示例代碼:
          1     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
          2     dispatch_async(queue, ^{
          3         //數(shù)據(jù)讀取
          4     });
          5     dispatch_async(queue, ^{
          6         //數(shù)據(jù)讀取2
          7     });
          8     dispatch_async(queue, ^{
          9         //數(shù)據(jù)寫入
          10     });
          11     dispatch_async(queue, ^{
          12         //數(shù)據(jù)讀取3
          13     });
          14     dispatch_async(queue, ^{
          15         //數(shù)據(jù)讀取4
          16     });
            顯然,這5個(gè)操作的執(zhí)行順序是我們無(wú)法預(yù)期的,我們希望在讀取1和讀取2執(zhí)行結(jié)束后,再執(zhí)行寫入,寫入完成后再執(zhí)行讀取3和讀取4。
            為了實(shí)現(xiàn)這個(gè)效果,這里可以使用GCD的另一個(gè)API:
            1     dispatch_barrier_async(queue, ^{
            2         //數(shù)據(jù)寫入
            3     });
            這樣就保證的寫入操作的并發(fā)安全性。
            對(duì)于沒有數(shù)據(jù)競(jìng)爭(zhēng)的并行操作,則可以使用并行隊(duì)列(CONCURRENT)來(lái)實(shí)現(xiàn)。
          JOIN行為
            CGD利用dispatch_group_wait來(lái)實(shí)現(xiàn)多個(gè)操作的join行為,代碼如下:
          1     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
          2     dispatch_group_t group = dispatch_group_create();
          3
          4     dispatch_group_async(group, queue, ^{
          5         sleep(0.5);
          6         NSLog(@"1");
          7     });
          8     dispatch_group_async(group, queue, ^{
          9         sleep(1.5);
          10         NSLog(@"2");
          11     });
          12     dispatch_group_async(group, queue, ^{
          13         sleep(2.5);
          14         NSLog(@"3");
          15     });
          16
          17     NSLog(@"aaaaa");
          18
          19     dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 2ull * NSEC_PER_SEC);
          20     if (dispatch_group_wait(group, time) == 0) {
          21         NSLog(@"已經(jīng)全部執(zhí)行完畢");
          22     }
          23     else {
          24         NSLog(@"沒有執(zhí)行完畢");
          25     }
          26
          27     NSLog(@"bbbbb");
            這里起了3個(gè)異步線程放在一個(gè)組里,之后通過(guò)dispatch_time_t創(chuàng)建了一個(gè)超時(shí)時(shí)間(2秒),程序之后行,立即輸出了aaaaa,這是主線程輸出的,當(dāng)遇到dispatch_group_wait時(shí),主線程會(huì)被掛起,等待2秒,在等待的過(guò)程當(dāng)中,子線程分別輸出了1和2,2秒時(shí)間達(dá)到后,主線程發(fā)現(xiàn)組里的任務(wù)并沒有全部結(jié)束,然后輸出了bbbbb。
            在這里,如果超時(shí)時(shí)間設(shè)置得比較長(zhǎng)(比如5秒),那么會(huì)在2.5秒時(shí)第三個(gè)任務(wù)結(jié)束后,立即輸出bbbbb,也就是說(shuō),當(dāng)組中的任務(wù)全部執(zhí)行完畢時(shí),主線程就不再被阻塞了。
            如果希望永久等待下去,時(shí)間可以設(shè)置為DISPATCH_TIME_FOREVER。
            并行循環(huán)
            類似于C#的PLINQ,OC也可以讓循環(huán)并行執(zhí)行,在GCD當(dāng)中有一個(gè)dispatch_apply函數(shù):
            1     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
            2     dispatch_apply(20, queue, ^(size_t i) {
            3         NSLog(@"%lu", i);
            4     });
            這段代碼讓i并行循環(huán)了20次,如果內(nèi)部處理的是一個(gè)數(shù)組,就可以實(shí)現(xiàn)對(duì)數(shù)組的并行循環(huán)了,它的內(nèi)部是dispatch_sync的同步操作,所以在執(zhí)行這個(gè)循環(huán)的過(guò)程當(dāng)中,當(dāng)前線程會(huì)被阻塞。
            暫停和恢復(fù)
            使用dispatch_suspend(queue)可以暫停隊(duì)列中任務(wù)的執(zhí)行,使用dispatch_result(queue)可以繼續(xù)執(zhí)行被暫停的隊(duì)列。

          posted on 2014-08-01 09:46 順其自然EVO 閱讀(24949) 評(píng)論(2)  編輯  收藏 所屬分類: 測(cè)試學(xué)習(xí)專欄

          評(píng)論

          # re: iOS開發(fā)之GCD使用總結(jié) 2015-04-28 17:30 sdsd

          用barrier只能是用戶隊(duì)列,不能是全局隊(duì)列  回復(fù)  更多評(píng)論   

          # re: iOS開發(fā)之GCD使用總結(jié) 2016-03-09 23:57 yl

          寫的很棒!謝謝!  回復(fù)  更多評(píng)論   

          <2014年8月>
          272829303112
          3456789
          10111213141516
          17181920212223
          24252627282930
          31123456

          導(dǎo)航

          統(tǒng)計(jì)

          • 隨筆 - 3936
          • 文章 - 404
          • 評(píng)論 - 179
          • 引用 - 0

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 乐至县| 佛坪县| 隆昌县| 襄汾县| 固原市| 平南县| 吴堡县| 鄂托克前旗| 嘉兴市| 淅川县| 深圳市| 钟祥市| 藁城市| 长阳| 吴桥县| 区。| 莎车县| 叶城县| 甘洛县| 廉江市| 河源市| 满洲里市| 威宁| 安岳县| 黑河市| 辛集市| 永德县| 蕲春县| 贡山| 临桂县| 伽师县| 绥芬河市| 长宁县| 广西| 同江市| 突泉县| 霍山县| 宜都市| 崇左市| 巧家县| 泸水县|