<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    qileilove

    blog已經轉移至github,大家請訪問 http://qaseven.github.io/

    iOS開發之GCD使用總結

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

    posted on 2014-08-01 09:46 順其自然EVO 閱讀(24943) 評論(2)  編輯  收藏 所屬分類: 測試學習專欄

    評論

    # re: iOS開發之GCD使用總結 2015-04-28 17:30 sdsd

    用barrier只能是用戶隊列,不能是全局隊列  回復  更多評論   

    # re: iOS開發之GCD使用總結 2016-03-09 23:57 yl

    寫的很棒!謝謝!  回復  更多評論   

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

    導航

    統計

    常用鏈接

    留言簿(55)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲激情视频网站| 毛片在线全部免费观看| 国产a v无码专区亚洲av| 国产成人久久AV免费| 亚洲天然素人无码专区| 亚洲日韩在线观看免费视频| 无码中文字幕av免费放dvd| 性xxxx黑人与亚洲| 国产国拍亚洲精品福利| 性xxxxx免费视频播放| 人妻免费久久久久久久了| 亚洲宅男天堂a在线| 亚洲国产综合精品中文字幕| 在线日本高清免费不卡| 国产精品亚洲一区二区无码| 亚洲日本在线观看| 国产国产人免费视频成69大陆| 无码午夜成人1000部免费视频| 精品久久久久亚洲| 亚洲高清资源在线观看| 亚洲男人在线无码视频| 亚洲中文无码永久免费| 久久精品乱子伦免费| 国产精品亚洲色婷婷99久久精品| 亚洲影院在线观看| 亚洲精品午夜无码专区| 曰皮全部过程视频免费国产30分钟| 污视频在线观看免费| 深夜免费在线视频| 亚洲国产成人久久精品软件| 色拍自拍亚洲综合图区| 亚洲国产婷婷六月丁香| 免费播放春色aⅴ视频| 全免费毛片在线播放| 久久国产精品2020免费m3u8| 精品视频免费在线| 亚洲精品无码中文久久字幕| 亚洲自偷精品视频自拍| 亚洲熟妇av一区二区三区| 国产gav成人免费播放视频| 成年网站免费视频A在线双飞|