iOS 多線程(四)GCD

一鹉究、GCD簡介

??? iOS開發(fā)中多線程的API主要有pthread自赔,NSThread匿级,NSOperationGCD津函,前兩者在現(xiàn)在開發(fā)過程中已經(jīng)不常用尔苦,NSOperation是面向?qū)ο蠓庋b的一套API允坚,而GCD則是一套純C語言API稠项。
???GCDGrand Central Dispatch)是Apple開發(fā)的一個(gè)多核編程的解決方法展运,它主要用于優(yōu)化應(yīng)用程序以支持多核處理器以及其他對稱多處理系統(tǒng)拗胜,它是一個(gè)在線程池模式的基礎(chǔ)上執(zhí)行的并行任務(wù)锈遥。

二所灸、Dispatch_Queue(隊(duì)列)

1庆寺、隊(duì)列:

????GCD中的隊(duì)列和數(shù)據(jù)結(jié)構(gòu)中的隊(duì)列特性上一致,都是受限制的線性表陵霉,遵循FIFO(First In First Out)踊挠,即新的任務(wù)需要在隊(duì)尾加入效床,讀取任務(wù)則從隊(duì)首部讀取,即先進(jìn)先出
(1)串行隊(duì)列(Serial Dispatch Queue
????串行隊(duì)列就是順序執(zhí)行任務(wù)沪猴,每次執(zhí)行一個(gè)运嗜,上一個(gè)任務(wù)執(zhí)行完畢后執(zhí)行下一個(gè)任務(wù)
(2)并發(fā)隊(duì)列(Concurrent Dispatch Queue
????并發(fā)隊(duì)列就是同時(shí)執(zhí)行多個(gè)任務(wù)担租,需要注意的是翩活,這些任務(wù)會按照被添加的順序依次開始執(zhí)行菠镇。但是任務(wù)完成的順序是任意的利耍。

2、任務(wù):

??任務(wù):在 GCD 里指的是 Block轴猎,即一段需要執(zhí)行的代碼塊
(1)同步執(zhí)行(dispatch_sync)
????完成任務(wù)后才會返回捻脖,進(jìn)行下一任務(wù)可婶,可見同步不具備開啟線程能力,只會在當(dāng)前線程依次執(zhí)行
(2)異步執(zhí)行(dispatch_async)
????完成任務(wù)后立即返回具温,進(jìn)行下一任務(wù),具備多線程能力

注意點(diǎn):并發(fā)隊(duì)列只會在異步執(zhí)行下生效筐赔,同步執(zhí)行不會觸發(fā)多線程創(chuàng)建铣猩。

三、GCD隊(duì)列編程實(shí)現(xiàn)

1川陆、多線程創(chuàng)建

(1)創(chuàng)建自定義隊(duì)列

/*
dispatch_queue_create 創(chuàng)建一個(gè) dispatch_queue_t 隊(duì)列 剂习,第一個(gè)參數(shù)設(shè)置該隊(duì)列標(biāo)識符,用于調(diào)試使用较沪,第二個(gè)參數(shù)们何,則是隊(duì)列類型
DISPATCH_QUEUE_SERIAL串行隊(duì)列
DISPATCH_QUEUE_CONCURRENT 并發(fā)隊(duì)列
*/
//串行隊(duì)列
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
//并發(fā)隊(duì)列
dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

(2)系統(tǒng)隊(duì)列:主線程隊(duì)列和全局隊(duì)列

  • 主線程隊(duì)列:這個(gè)是主線程的串行隊(duì)列
  • 全局隊(duì)列:這是個(gè)全局的并發(fā)隊(duì)列鹦蠕,很多時(shí)候可以不需要自己創(chuàng)建并發(fā)隊(duì)列,直接獲取全局隊(duì)列即可 第一個(gè)參數(shù)為優(yōu)先級,這是個(gè)大概優(yōu)先級設(shè)置
/**
  DISPATCH_QUEUE_PRIORITY_HIGH //高優(yōu)先級
  DISPATCH_QUEUE_PRIORITY_DEFAULT //默認(rèn)優(yōu)先級
  DISPATCH_QUEUE_PRIORITY_LOW //低優(yōu)先級
  DISPATCH_QUEUE_PRIORITY_BACKGROUND //后臺優(yōu)先級
*/
//主線程隊(duì)列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//全局隊(duì)列
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

(3)隊(duì)列中添加任務(wù)執(zhí)行

//異步執(zhí)行
dispatch_async(serialQueue, ^{
    NSLog(@"serialQueue中異步執(zhí)行");
});

//同步執(zhí)行
dispatch_sync(serialQueue, ^{
    NSLog(@"serialQueue中同步執(zhí)行");
});

(4)總共為3種隊(duì)列類型噪伊,6種組合方式
WechatIMG96.png
2拙寡、3種類型6種組合方式介紹
(1)主線程同步執(zhí)行(死鎖)
代碼:
NSLog(@"Begin");
//主線程同步同步執(zhí)行
dispatch_sync(mainQueue, ^{
  NSLog(@"主線程同步同步執(zhí)行");
});
NSLog(@"End");

運(yùn)行結(jié)果:
2017-05-03 16:50:31.542 GCDDemo[41489:596607] Begin

??只打印了begin诚啃,并沒有執(zhí)行下去,事實(shí)上這里發(fā)生了死鎖。首先代碼是在主線程執(zhí)行,主線程是串行隊(duì)列,而dispatch_sync是同步執(zhí)行尚洽,sync添加的任務(wù)需要執(zhí)行需要等待NSLog(@"End");執(zhí)行完畢,NSLog(@"End");任務(wù)本身也添加在主線程隊(duì)列中,所以執(zhí)行這個(gè)任務(wù)的前提是sync添加的任務(wù)執(zhí)行完畢,這就出現(xiàn)了兩個(gè)任務(wù)互相等待,造成死鎖
??總結(jié):主線程中執(zhí)行同步任務(wù)會發(fā)生死鎖,即:串行隊(duì)列中嵌套串行隊(duì)列任務(wù)會發(fā)生死鎖

(2)主線程異步執(zhí)行
代碼:
NSLog(@"Begin"); 
//主線程異步執(zhí)行 
dispatch_async(mainQueue, ^{ 
for (int i = 0; i < 3; i ++) { 
    NSLog(@"%d---------主線程異步執(zhí)行%@", i,[NSThread currentThread]); 
  } 
});

dispatch_async(mainQueue, ^{ 
  for (int i = 0; i < 3; i ++) { 
    NSLog(@"%d---------主線程異步執(zhí)行%@", i,[NSThread currentThread]); 
  } 
});

dispatch_async(mainQueue, ^{ 
   for (int i = 0; i < 3; i ++) { 
    NSLog(@"%d---------主線程異步執(zhí)行%@", i,[NSThread currentThread]); 
  } 
}); 

NSLog(@"End"); 

運(yùn)行結(jié)果:
2017-05-03 17:09:26.031 GCDDemo[42671:613068] Begin
2017-05-03 17:09:26.031 GCDDemo[42671:613068] End
2017-05-03 17:09:26.042 GCDDemo[42671:613068] 0---------主線程異步執(zhí)行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.043 GCDDemo[42671:613068] 1---------主線程異步執(zhí)行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.044 GCDDemo[42671:613068] 2---------主線程異步執(zhí)行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.052 GCDDemo[42671:613068] 0---------主線程異步執(zhí)行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.057 GCDDemo[42671:613068] 1---------主線程異步執(zhí)行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.059 GCDDemo[42671:613068] 2---------主線程異步執(zhí)行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.060 GCDDemo[42671:613068] 0---------主線程異步執(zhí)行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.062 GCDDemo[42671:613068] 1---------主線程異步執(zhí)行<NSThread: 0x60000007d5c0>{number = 1, name = main}
2017-05-03 17:09:26.063 GCDDemo[42671:613068] 2---------主線程異步執(zhí)行<NSThread: 0x60000007d5c0>{number = 1, name = main}

總結(jié):主線程隊(duì)列異步執(zhí)行不會開辟線程,會在當(dāng)前線程同步執(zhí)行。

(3)串行隊(duì)列同步執(zhí)行
代碼:
NSLog(@"Begin"); 
//串行隊(duì)列同步執(zhí)行 
dispatch_sync(serialQueue, ^{ 
  for (int i = 0; i < 3; i ++) { 
    NSLog(@"%d---------串行隊(duì)列同步執(zhí)行%@", i,[NSThread currentThread]); 
  } 
});

dispatch_sync(serialQueue, ^{ 
  for (int i = 0; i < 3; i ++) { 
    NSLog(@"%d---------串行隊(duì)列同步執(zhí)行%@", i,[NSThread currentThread]); 
  } 
});

dispatch_sync(serialQueue, ^{ 
  for (int i = 0; i < 3; i ++) { 
    NSLog(@"%d---------串行隊(duì)列同步執(zhí)行%@", i,[NSThread currentThread]); 
  } 
});

NSLog(@"End");

運(yùn)行結(jié)果: 
2017-05-03 17:25:48.225 GCDDemo[43694:625941] Begin
2017-05-03 17:25:48.225 GCDDemo[43694:625941] 0---------串行隊(duì)列同步執(zhí)行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.226 GCDDemo[43694:625941] 1---------串行隊(duì)列同步執(zhí)行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.226 GCDDemo[43694:625941] 2---------串行隊(duì)列同步執(zhí)行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.226 GCDDemo[43694:625941] 0---------串行隊(duì)列同步執(zhí)行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.227 GCDDemo[43694:625941] 1---------串行隊(duì)列同步執(zhí)行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.227 GCDDemo[43694:625941] 2---------串行隊(duì)列同步執(zhí)行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.227 GCDDemo[43694:625941] 0---------串行隊(duì)列同步執(zhí)行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.228 GCDDemo[43694:625941] 1---------串行隊(duì)列同步執(zhí)行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.228 GCDDemo[43694:625941] 2---------串行隊(duì)列同步執(zhí)行<NSThread: 0x60800007f880>{number = 1, name = main}
2017-05-03 17:25:48.228 GCDDemo[43694:625941] End

??總結(jié): 串行隊(duì)列異步執(zhí)行不會開辟多線程,只會在一條線程中依次執(zhí)行

(4)串行隊(duì)列異步執(zhí)行
代碼:
NSLog(@"Begin"); 
//串行隊(duì)列異步執(zhí)行 
dispatch_async(serialQueue, ^{ 
  for (int i = 0; i < 3; i ++) { 
    NSLog(@"%d---------串行隊(duì)列異步執(zhí)行%@", i,[NSThread currentThread]); 
  } 
});

dispatch_async(serialQueue, ^{ 
  for (int i = 0; i < 3; i ++) { 
    NSLog(@"%d---------串行隊(duì)列異步執(zhí)行%@", i,[NSThread currentThread]); 
  } 
});

dispatch_async(serialQueue, ^{ 
  for (int i = 0; i < 3; i ++) { 
    NSLog(@"%d---------串行隊(duì)列異步執(zhí)行%@", i,[NSThread currentThread]); 
  } 
});

NSLog(@"End");

運(yùn)行結(jié)果:
2017-05-03 17:20:16.514 GCDDemo[43349:621375] Begin
2017-05-03 17:20:16.514 GCDDemo[43349:621375] End
2017-05-03 17:20:16.515 GCDDemo[43349:621680] 0---------串行隊(duì)列異步執(zhí)行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.515 GCDDemo[43349:621680] 1---------串行隊(duì)列同步執(zhí)行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.515 GCDDemo[43349:621680] 2---------串行隊(duì)列異步執(zhí)行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.516 GCDDemo[43349:621680] 0---------串行隊(duì)列異步執(zhí)行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.516 GCDDemo[43349:621680] 1---------串行隊(duì)列異步執(zhí)行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.516 GCDDemo[43349:621680] 2---------串行隊(duì)列異步執(zhí)行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.516 GCDDemo[43349:621680] 0---------串行隊(duì)列異步執(zhí)行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.517 GCDDemo[43349:621680] 1---------串行隊(duì)列異步執(zhí)行<NSThread: 0x608000268040>{number = 3, name = (null)}
2017-05-03 17:20:16.517 GCDDemo[43349:621680] 2---------串行隊(duì)列異步執(zhí)行<NSThread: 0x608000268040>{number = 3, name = (null)}

??總結(jié): 串行隊(duì)列異步執(zhí)行不會開辟多線程摇肌,只會在一條線程中依次執(zhí)行
??串行隊(duì)列同步和異步執(zhí)行都沒有開辟多線程肯适,在一條線程中同步執(zhí)行赎婚,那么對于串行隊(duì)列同步和異步執(zhí)行有什么區(qū)別呢?
??區(qū)別只有一點(diǎn):
??dispatch_async:不會阻塞當(dāng)前隊(duì)列,立即返回添加當(dāng)前隊(duì)列后面任務(wù)绢馍,可以看到上圖打印結(jié)果,先打印end搁痛。再打印async任務(wù)
??dispatch_sync:會阻塞當(dāng)前隊(duì)列,等該sync任務(wù)全部執(zhí)行完畢之后再添加當(dāng)前隊(duì)列后面任務(wù)抽碌,可以看到上圖打印結(jié)果痴颊,先打印完sync任務(wù)打印end抛丽。

(6)并發(fā)隊(duì)列同步執(zhí)行
NSLog(@"Begin"); 
//串行隊(duì)列同步執(zhí)行 
dispatch_sync(concurrentQueue, ^{ 
  for (int i = 0; i < 3; i ++) { 
    NSLog(@"%d---------并發(fā)隊(duì)列同步執(zhí)行%@", i,[NSThread currentThread]); 
  } 
});

dispatch_sync(concurrentQueue, ^{ 
  for (int i = 0; i < 3; i ++) { 
    NSLog(@"%d---------并發(fā)隊(duì)列同步執(zhí)行%@", i,[NSThread currentThread]); 
  } 
});

dispatch_sync(concurrentQueue, ^{ 
  for (int i = 0; i < 3; i ++) { 
    NSLog(@"%d---------并發(fā)隊(duì)列同步執(zhí)行%@", i,[NSThread currentThread]); 
  } 
});

NSLog(@"End");

運(yùn)行結(jié)果:
2017-05-03 17:34:24.407 GCDDemo[44222:633000] Begin 
2017-05-03 17:34:24.407 GCDDemo[44222:633000] 0---------并發(fā)隊(duì)列同步執(zhí)行<NSThread: 0x60800006f380>{number = 1, name = main} 
2017-05-03 17:34:24.407 GCDDemo[44222:633000] 1---------并發(fā)隊(duì)列同步執(zhí)行<NSThread: 0x60800006f380>{number = 1, name = main} 
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 2---------并發(fā)隊(duì)列同步執(zhí)行<NSThread: 0x60800006f380>{number = 1, name = main} 
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 0---------并發(fā)隊(duì)列同步執(zhí)行<NSThread: 0x60800006f380>{number = 1, name = main} 
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 1---------并發(fā)隊(duì)列同步執(zhí)行<NSThread: 0x60800006f380>{number = 1, name = main} 
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 2---------并發(fā)隊(duì)列同步執(zhí)行<NSThread: 0x60800006f380>{number = 1, name = main} 
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 0---------并發(fā)隊(duì)列同步執(zhí)行<NSThread: 0x60800006f380>{number = 1, name = main} 
2017-05-03 17:34:24.408 GCDDemo[44222:633000] 1---------并發(fā)隊(duì)列同步執(zhí)行<NSThread: 0x60800006f380>{number = 1, name = main} 
2017-05-03 17:34:24.409 GCDDemo[44222:633000] 2---------并發(fā)隊(duì)列同步執(zhí)行<NSThread: 0x60800006f380>{number = 1, name = main} 
2017-05-03 17:34:24.410 GCDDemo[44222:633000] End

總結(jié): 并發(fā)隊(duì)列同步執(zhí)行不會開辟多線程,只會在一條線程中依次執(zhí)行

(6)并發(fā)隊(duì)列異步執(zhí)行
NSLog(@"Begin"); 
//并發(fā)隊(duì)列異步執(zhí)行 
dispatch_async(concurrentQueue, ^{ 
  for (int i = 0; i < 3; i ++) { 
    NSLog(@"%d---------并發(fā)隊(duì)列異步執(zhí)行%@", i,[NSThread currentThread]); 
  } 
});

dispatch_async(concurrentQueue, ^{ 
  for (int i = 0; i < 3; i ++) { 
    NSLog(@"%d---------并發(fā)隊(duì)列異步執(zhí)行%@", i,[NSThread currentThread]); 
  } 
});

dispatch_async(concurrentQueue, ^{ 
  for (int i = 0; i < 3; i ++) { 
    NSLog(@"%d---------并發(fā)隊(duì)列異步執(zhí)行%@", i,[NSThread  currentThread]); 
  } 
});

NSLog(@"End");

運(yùn)行結(jié)果:
2017-05-03 17:38:10.893 GCDDemo[44461:636216] Begin
2017-05-03 17:38:10.894 GCDDemo[44461:636216] End
2017-05-03 17:38:10.894 GCDDemo[44461:636252] 0---------并發(fā)隊(duì)列異步執(zhí)行<NSThread: 0x60800007a700>{number = 4, name = (null)}
2017-05-03 17:38:10.894 GCDDemo[44461:636254] 0---------并發(fā)隊(duì)列異步執(zhí)行<NSThread: 0x60800007ac40>{number = 5, name = (null)}
2017-05-03 17:38:10.894 GCDDemo[44461:636251] 0---------并發(fā)隊(duì)列異步執(zhí)行<NSThread: 0x60800007a800>{number = 3, name = (null)}
2017-05-03 17:38:10.897 GCDDemo[44461:636252] 1---------并發(fā)隊(duì)列異步執(zhí)行<NSThread: 0x60800007a700>{number = 4, name = (null)}
2017-05-03 17:38:10.899 GCDDemo[44461:636254] 1---------并發(fā)隊(duì)列異步執(zhí)行<NSThread: 0x60800007ac40>{number = 5, name = (null)}
2017-05-03 17:38:10.900 GCDDemo[44461:636251] 1---------并發(fā)隊(duì)列異步執(zhí)行<NSThread: 0x60800007a800>{number = 3, name = (null)}
2017-05-03 17:38:10.903 GCDDemo[44461:636252] 2---------并發(fā)隊(duì)列異步執(zhí)行<NSThread: 0x60800007a700>{number = 4, name = (null)}
2017-05-03 17:38:10.904 GCDDemo[44461:636254] 2---------并發(fā)隊(duì)列異步執(zhí)行<NSThread: 0x60800007ac40>{number = 5, name = (null)}
2017-05-03 17:38:10.905 GCDDemo[44461:636251] 2---------并發(fā)隊(duì)列異步執(zhí)行<NSThread: 0x60800007a800>{number = 3, name = (null)}

??總結(jié): 并發(fā)隊(duì)列異步執(zhí)行會開辟多線程執(zhí)行,并且執(zhí)行順序不定

四惕鼓、應(yīng)用示例

異步處理數(shù)據(jù)完成后汞幢,主線程更新UI界面

dispatch_async(globalQueue, ^{
  //異步數(shù)據(jù)處理...
  dispatch_async(mainQueue, ^{
    //主線程更新UI
  });
});

??可以看到運(yùn)用GCD可以輕松的進(jìn)行線程間通信,使用指定隊(duì)列進(jìn)行串行處理任務(wù),例如數(shù)據(jù)庫存儲等依賴線程安全等處理

//串行隊(duì)列保證線程安全
dispatch_sync(serialQueue, ^{
  //數(shù)據(jù)存儲等依賴線程安全操作...
});

??FMDBDataBaseQueue就是使用的串行隊(duì)列來保證線程安全的

五似嗤、其他GCD API

1每聪、dispatch_after
dispatch_time_t time=dispatch_time(DISPATCH_TIME_NOW, 10*NSEC_PER_SEC);
  dispatch_after(time, dispatch_get_main_queue(), ^{
    NSLog(@"After");
});

??延遲執(zhí)行系谐,有時(shí)候可以作為定時(shí)執(zhí)行作用,需要注意的是嗦董,該函數(shù)并不是在指定時(shí)間后執(zhí)行處理,而只是在指定時(shí)間追加處理到dispatch_queue,實(shí)際執(zhí)行時(shí)間受到runloop的狀態(tài)影響宪卿,存在偏差芭碍。

2杉女、dispatch_once
static dispatch_once_t onceToken;
 dispatch_once(&onceToken, ^{
   sharedManager = [[SchoolManager alloc] init];
 });

??執(zhí)行一次,在block中的代碼全局只會執(zhí)行一次猴蹂,被廣泛用于單例創(chuàng)建中

3撮躁、dispatch_suspend / dispatch_resume
dispatch_suspend(queque); //掛起隊(duì)列  
dispatch_resume(queue);//恢復(fù)隊(duì)列 

??掛起對已經(jīng)執(zhí)行的任務(wù)沒有影響,會暫停所有未執(zhí)行的任務(wù)以及后續(xù)追加的任務(wù)浪慌,恢復(fù)則會繼續(xù)執(zhí)行所有被掛起的任務(wù)

4槽唾、dispatch_set_target_queue
//搬運(yùn)一段代碼
dispatch_queue_t mySerialDispatchQueue =
dispatch_queue_create("com.example.gcd.MySerialDispatchQueue", NULL);
dispatch_queue_t globalDispatchQueueBackground =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_set_target_queue(mySerialDispatchQueue, globalDispatchQueueBackground);

??dispatch_set_target_queue主要有兩個(gè)作用:
??(1)設(shè)置優(yōu)先級:自建的隊(duì)列優(yōu)先級默認(rèn)和系統(tǒng)隊(duì)列優(yōu)先級一致齐佳,設(shè)置參數(shù)1隊(duì)列的優(yōu)先級和參數(shù)2的優(yōu)先級一致私恬,顯然你不能設(shè)置系統(tǒng)全局隊(duì)列和主隊(duì)列優(yōu)先級
??(2)更改隊(duì)列的執(zhí)行層級:如果多個(gè)串行隊(duì)列設(shè)置函數(shù)目標(biāo)串行隊(duì)列是某一個(gè)串行隊(duì)列,原本并發(fā)執(zhí)行的串行隊(duì)列炼吴,在目標(biāo)串行隊(duì)列上只能依次執(zhí)行践付,代碼示例如下

dispatch_queue_t targetQueue = dispatch_queue_create("test.target.queue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);

dispatch_set_target_queue(queue1, targetQueue);
dispatch_set_target_queue(queue2, targetQueue);
dispatch_set_target_queue(queue3, targetQueue);
dispatch_async(queue1, ^{
  NSLog(@"1 in");
  [NSThread sleepForTimeInterval:3.f];
  NSLog(@"1 out");
});

dispatch_async(queue2, ^{
  NSLog(@"2 in");
  [NSThread sleepForTimeInterval:2.f];
  NSLog(@"2 out");
});

dispatch_async(queue3, ^{
  NSLog(@"3 in");
  [NSThread sleepForTimeInterval:1.f];
  NSLog(@"3 out");
});
5、dispatch_group

??主要應(yīng)對這樣的需求缺厉,異步處理完A和B任務(wù),兩者都執(zhí)行完執(zhí)行C任務(wù)隧土,和NSOperation中的依賴一致提针。示例如下

dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  //異步耗時(shí)操作A
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  //異步耗時(shí)操作B
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
  // 前面的異步操作A和B都執(zhí)行完之后,回主線程
});
6曹傀、dispatch_apply

??這是dispatch_syncdispatch_group的關(guān)聯(lián)API辐脖,按指定次數(shù)將指定的Block追加到指定的Dispatch_Queue中,并且等待全部執(zhí)行結(jié)束皆愉∈燃郏可以用于遍歷效果

//全局隊(duì)列
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, globalQueue, ^(size_t index) {
  //重復(fù)執(zhí)行10次
  NSLog(@"%zu",index);
});
//10次執(zhí)行完之后艇抠,再執(zhí)行Done
NSLog(@"Done");
6、dispatch_barrier_sync / dispatch_barrier_async

??柵欄:有時(shí)候創(chuàng)建兩組并發(fā)任務(wù)久锥,如果在中間加入柵欄家淤,那么這個(gè)任務(wù)會在第一組任務(wù)完成后執(zhí)行,并且第二組任務(wù)會在柵欄任務(wù)完成后才開始執(zhí)行瑟由,如下圖所示在并發(fā)隊(duì)列中添加任務(wù)絮重,執(zhí)行順序一定是:任務(wù)組A -> Barrier任務(wù) -> 任務(wù)組B,
示例代碼如下:

dispatch_queue_t concurrentQueue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
  NSLog(@"1---------");
});
dispatch_async(concurrentQueue, ^{
  NSLog(@"2--------");
});
dispatch_barrier_async(concurrentQueue, ^{
  NSLog(@"barrier--------");
});
dispatch_async(concurrentQueue, ^{
  NSLog(@"3--------");
});
dispatch_async(concurrentQueue, ^{
  NSLog(@"4--------");
});

??打印執(zhí)行順序1歹苦,2不定青伤,3,4也不定殴瘦,但是barrier一定在1和2之后狠角,3和4一定在barrier之后,可以自行添加數(shù)量測試蚪腋。
??應(yīng)用場景丰歌,經(jīng)常我們會自行創(chuàng)建一個(gè)隊(duì)列進(jìn)行文件讀取和存儲,一般文件讀取的速度很快辣吃,可以使用并發(fā)隊(duì)列多線程提高讀取效率动遭,但是文件存儲需要考慮到線程安全,那么我們就可以使用barrier進(jìn)行文件存儲操作神得,類似這樣

dispatch_queue_t concurrentQueue = dispatch_queue_create("ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
  //文件讀取
});
dispatch_async(concurrentQueue, ^{
  //文件讀取
});
dispatch_barrier_async(concurrentQueue, ^{
  //文件存儲
});
dispatch_barrier_async(concurrentQueue, ^{
  //文件存儲
});
dispatch_async(concurrentQueue, ^{
  //文件讀取
});

??可見使用barrier可以輕松高效的實(shí)現(xiàn)文件IO厘惦。
??dispatch_barrier需要注意的點(diǎn),dispatch_barrier只會對自建的隊(duì)列生效哩簿,對于系統(tǒng)的mainQueueGlobalQueue不起作用
??dispatch_barrier_asyncdispatch_barrier_sync的區(qū)別也同樣在于同步和異步宵蕉,dispatch_barrier_async不會等待自己任務(wù)執(zhí)行完畢才會在隊(duì)列中添加其他任務(wù),而dispatch_barrier_sync會等待自己任務(wù)執(zhí)行完畢后才會在隊(duì)列中添加其他任務(wù)节榜。
??AFNetworking中大量使用dispatch_barrier_async做數(shù)據(jù)存儲羡玛,可以看到dispatch_barrier_async也可以實(shí)現(xiàn)串行同步隊(duì)列效果,相比于dispatch_sync容易產(chǎn)生死鎖(在串行隊(duì)列中同步添加該串行隊(duì)列任務(wù)即會發(fā)生死鎖)宗苍,dispatch_barrier_async更加安全稼稿。

五、Dispatch Semaphore 信號量

??dispatch_semaphore_t信號量本質(zhì)上是一種鎖讳窟。
??下面我們看下信號量的使用:dispatch_semaphore_t的作用之一解決資源搶奪問題
??對于數(shù)據(jù)存儲類似數(shù)據(jù)庫让歼,非原子性可變字典和可變數(shù)組等多線程下不安全的操作,可以使用同步隊(duì)列保證線程安全丽啡,那么在并發(fā)隊(duì)列中谋右,可以使用信號量來解決資源搶奪問題

//全局隊(duì)列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
//創(chuàng)建一個(gè)信號量,初始值為1 
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1) ; //創(chuàng)建可變數(shù)組 
NSMutableArray *array = [[NSMutableArray alloc] init];
 for  (int i = 0; i< 1000; ++i)  { 
  dispatch_async(queue, ^{ 
    //這里會一直等待补箍,直到信號量大于等于1
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) ; 
    //執(zhí)行到這里改执,消費(fèi)一個(gè)信號量 
    NSLog(@"%@",[NSThread currentThread]);
    [array addObject:[NSNumber numberWithInt:i]]; 
    //這里增加一個(gè)信號量 
    dispatch_semaphore_signal(semaphore); 
    }); 
}

代碼解讀:
??dispatch_semaphore_create(1) 創(chuàng)建了值為1信號量
??dispatch_semaphore_wait 啸蜜,如果信號量的值大于等于1,那么辈挂,信號量值減1衬横,然后向下執(zhí)行,如果信號量值為0呢岗,一直等待冕香。直到大于等于1的時(shí)候,率先進(jìn)入等待狀態(tài)的異步隊(duì)列率先執(zhí)行
??dispatch_semaphore_signal信號量值加1
??實(shí)際這種效果和加鎖的本質(zhì)一致后豫,dispatch_semaphore_t的另外一個(gè)作用就是可以控制線程并發(fā)數(shù)量悉尾,iOS7之后系統(tǒng)自動(dòng)開辟的線程數(shù)量可以多達(dá)60-70,而GCD中并沒有提供控制線程數(shù)量的API挫酿,NSOperation中可以設(shè)置最大線程數(shù)构眯。

??下面我們使用信號量來實(shí)現(xiàn)一下線程數(shù)量控制:

//線程并發(fā)數(shù)限制 
static dispatch_semaphore_t limitSemaphore; 
//控制專用隊(duì)列 
static dispatch_queue_t serialQueue; 
//單例創(chuàng)建 
static dispatch_once_t onceToken; 
dispatch_once(&onceToken, ^{
  //設(shè)置最大線程并發(fā)數(shù)為5 
  limitCount = dispatch_semaphore_create(5);
  serialQueue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
}); 
dispatch_async(serialQueue, ^{ 
  //信號量>=1繼續(xù)執(zhí)行,否則等待 
  dispatch_semaphore_wait(limitSemaphore, DISPATCH_TIME_FOREVER);
  dispatch_async(queue, ^{
    //這里執(zhí)行一些任務(wù) 
    NSLog(@"%@",[NSThread currentThread]);
    //在該工作線程執(zhí)行完成后釋放信號量
    dispatch_semaphore_signal(limitSemaphore);
    }); 
});

六早龟、dispatch source

??dispatch source是一組不常用的GCD API惫霸。是BSD系內(nèi)核慣有功能kqueue的包裝。簡單來說葱弟,dispatch source是一個(gè)監(jiān)視某些類型事件的對象板惑。它支持所有kqueue所支持的事件以及mach(mach介紹可以看這里mach wikipedia)端口滥沫、內(nèi)建計(jì)時(shí)器支持和用戶事件,CPU負(fù)荷占用小,資源占用小翔横。
??dispatch sourc聯(lián)結(jié)流程:在任一線程上調(diào)用dispatch_source_merge_data 這個(gè)函數(shù)后畔勤,會執(zhí)行 Dispatch Source 事先定義好的句柄(可以簡單理解句柄就是block )(是不是有點(diǎn)通知懂盐,回調(diào)的味道哈)

??代碼展示:

//全局隊(duì)列 
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//創(chuàng)建source 
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue()); 
//定義source的句柄 
dispatch_source_set_event_handler(source, ^{
  //調(diào)用一次dispatch_source_merge_data會調(diào)用這個(gè)句柄  
  NSLog(@"%lu",dispatch_source_get_data(source)); 
}); 
//默認(rèn)source是suspend的北启,需要resume生效 dispatch_resume(source); 
//遍歷10次 
dispatch_apply(10, globalQueue, ^(size_t index) {
  // merge data dispatch_source_merge_data(source, 1); 
});

??這段程序簡單邏輯:調(diào)用dispatch_source_merge_data 會觸發(fā)實(shí)現(xiàn)定義好的事件

dispatch_source_set_event_handler(source, ^{ //調(diào)用一次dispatch_source_merge_data會調(diào)用這個(gè)句柄 NSLog(@"%lu",dispatch_source_get_data(source)); });
dispatch_source_create 函數(shù)參數(shù)DISPATCH_SOURCE_TYPE_DATA_ADD 累加

??當(dāng)注冊系統(tǒng)事件的時(shí)候,有時(shí)候系統(tǒng)還沒來得及通知應(yīng)用程序蝌麸,這個(gè)時(shí)候点寥,系統(tǒng)會累計(jì)傳遞過來的值
DISPATCH_SOURCE_TYPE_DATA_OR 邏輯或處理累計(jì)傳遞過來的值
其他:

DISPATCH_SOURCE_TYPE_MACH_SEND MACH端口發(fā)送
DISPATCH_SOURCE_TYPE_MACH_RECV MACH端口接收
DISPATCH_SOURCE_TYPE_PROC 監(jiān)測進(jìn)程相關(guān)事件
DISPATCH_SOURCE_TYPE_READ 可讀取文件映像
DISPATCH_SOURCE_TYPE_SIGNAL 接收信號
DISPATCH_SOURCE_TYPE_TIMER 定時(shí)器
DISPATCH_SOURCE_TYPE_VNODE 文件系統(tǒng)變更
DISPATCH_SOURCE_TYPE_WRITE 可寫入文件映像

參考博客:http://www.reibang.com/p/8920a3da98b8
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市来吩,隨后出現(xiàn)的幾起案子敢辩,更是在濱河造成了極大的恐慌,老刑警劉巖弟疆,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件责鳍,死亡現(xiàn)場離奇詭異,居然都是意外死亡兽间,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門正塌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嘀略,“玉大人恤溶,你說我怎么就攤上這事≈难颍” “怎么了咒程?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長讼育。 經(jīng)常有香客問我帐姻,道長,這世上最難降的妖魔是什么奶段? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任饥瓷,我火速辦了婚禮,結(jié)果婚禮上痹籍,老公的妹妹穿的比我還像新娘呢铆。我一直安慰自己,他們只是感情好蹲缠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布棺克。 她就那樣靜靜地躺著,像睡著了一般线定。 火紅的嫁衣襯著肌膚如雪娜谊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天斤讥,我揣著相機(jī)與錄音纱皆,去河邊找鬼。 笑死周偎,一個(gè)胖子當(dāng)著我的面吹牛抹剩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蓉坎,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼澳眷,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蛉艾?” 一聲冷哼從身側(cè)響起钳踊,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎勿侯,沒想到半個(gè)月后拓瞪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡助琐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年祭埂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蛆橡,死狀恐怖舌界,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情泰演,我是刑警寧澤呻拌,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站睦焕,受9級特大地震影響藐握,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜垃喊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一猾普、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧缔御,春花似錦抬闷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至眷茁,卻和暖如春炕泳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背上祈。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工培遵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人登刺。 一個(gè)月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓籽腕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親纸俭。 傳聞我的和親對象是個(gè)殘疾皇子皇耗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評論 2 354

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