GCD

概念解釋

1. 執(zhí)行任務(wù)的函數(shù):在GCD中介陶,任務(wù)是通過 block來封裝的,并且任務(wù)的block沒有參數(shù)也沒有返回值。

同步:你必須把我的代碼執(zhí)行完你再走山析,一定要執(zhí)行完同步里的代碼再執(zhí)行下面的代碼

void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

異步:你先走執(zhí)行我下面的代碼遥椿,我找人基矮、找線程去執(zhí)行我里面的代碼

void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

2. GCD使用步驟:

第一步: 創(chuàng)建/獲取 隊列
第二步: 創(chuàng)建任務(wù),確定要做的事情
第三步: 將任務(wù)添加到隊列中
(1)GCD會自動將隊列中的任務(wù)取出冠场,放到對應(yīng)的線程中執(zhí)行
(2)任務(wù)的取出遵循隊列的FIFO原則: 先進(jìn)先出家浇,后進(jìn)后出

3. 隊列

包括: 串行隊列、并發(fā)隊列碴裙、主隊列钢悲、全局隊列

1. 串行隊列(Serial Dispatch Queue)

串行隊列的特點:
以先進(jìn)先出的方式,按順序調(diào)度隊列中的任務(wù)去執(zhí)行莺琳,一次只能調(diào)度一個任務(wù)。
無論隊列中所指定的執(zhí)行任務(wù)的函數(shù)是同步還是異步诱贿,都必須等待前一個任務(wù)執(zhí)行完畢,才可以調(diào)度后面的任務(wù)咕缎。

串行隊列的創(chuàng)建:

(1) dispatch_queue_tqueue = dispatch_queue_create("itheima", DISPATCH_QUEUE_SERIAL);
(2) dispatch_queue_tqueue = dispatch_queue_create("itheima", NULL);

串行隊列珠十,同步執(zhí)行:
開不開線程? 不開線程焙蹭。
順序執(zhí)行還是亂序執(zhí)行? 順序執(zhí)行。

串行隊列嫂伞,異步執(zhí)行:
開不開線程? 只會開一條線程孔厉。
順序執(zhí)行還是亂序執(zhí)行? 順序執(zhí)行。

2. 并發(fā)隊列(Concurrent Dispatch Queue)
  • 并發(fā)隊列的特點:以先進(jìn)先出的方式撰豺,并發(fā)(同時)調(diào)度隊列中的任務(wù)去執(zhí)行拼余。
  • 如果當(dāng)前調(diào)度的任務(wù)是同步執(zhí)行的,會等待當(dāng)前任務(wù)執(zhí)行完畢后凡橱,再調(diào)度后續(xù)的任務(wù)亭姥。
  • 如果當(dāng)前調(diào)度的任務(wù)是異步執(zhí)行的,同時底層線程池有可用的線程資源坝撑,就不會等待當(dāng)前任務(wù)粮揉,直接調(diào)度任務(wù)到新線程去執(zhí)行。

并發(fā)隊列的創(chuàng)建:

dispatch_queue_t q = dispatch_queue_create("itheima", DISPATCH_QUEUE_CONCURRENT);

并發(fā)隊列,同步執(zhí)行:
開不開線程? 不開線程塔沃。
順序執(zhí)行還是亂序執(zhí)行? 順序執(zhí)行。
并發(fā)同步 和 串行同步的執(zhí)行結(jié)果一模一樣螃概。

并發(fā)隊列吊洼,異步執(zhí)行:
開不開線程? 開多條新線程。
順序執(zhí)行還是亂序執(zhí)行? 亂序執(zhí)行冒窍。

3. 主隊列:
  • 遇到主隊列,不管同步異步都要先執(zhí)行完主線程里的代碼再執(zhí)行主隊列里的代碼
  • dispatch_sync方法不能在主隊列中調(diào)用款慨,因為這會無限期的阻止線程并會導(dǎo)致你的應(yīng)用死鎖谬莹。所有通過GCD提交到主隊列的任務(wù)必須是異步的附帽。
  • 只有當(dāng)主線程空閑時, 主隊列才會調(diào)度任務(wù)到主線程執(zhí)行
  • 主隊列是系統(tǒng)提供的,無需自己創(chuàng)建整胃,可以直接通過dispatch_get_main_queue()函數(shù)來獲取慢显。
  • 主隊列的特點:先執(zhí)行完主線程上的代碼,才會執(zhí)行主隊列中的任務(wù)
    1屋灌、添加到主隊列的任務(wù)只能由主線程來執(zhí)行应狱。
    2疾呻、以先進(jìn)先出的方式,只有當(dāng)主線程的代碼執(zhí)行完畢后尉咕,主隊列才會調(diào)度任務(wù)到主線程執(zhí)行璃岳。
    如下列代碼執(zhí)行打印123后再打印hello
- (void)demo1 {    
for (int i = 0; i < 10; i++) {       
NSLog(@"111");        
dispatch_async(dispatch_get_main_queue(), ^{            
NSLog(@"hello %d  %@",i,[NSThread currentThread]);
        });        
NSLog(@"222");        
NSLog(@"333");    
}
}
  • 主隊列悔捶,同步執(zhí)行(死鎖)主線程和主隊列同步任務(wù)相互等待蜕该,造成死鎖洲鸠。
    執(zhí)行到同步主隊列后,同步時主線程想讓里面代碼順序執(zhí)行下去(直接進(jìn)去執(zhí)行里面代碼)绢淀,而主隊列要求執(zhí)行完畢主線程代碼再執(zhí)行里面代碼袜匿,導(dǎo)致誰都無法繼續(xù)執(zhí)行居灯,導(dǎo)致死鎖。
- (void)viewDidLoad {    
[super viewDidLoad];    
NSLog(@"begin");    
for (int i = 0; i < 10; i++) {        //死鎖义锥,一看同步就不分子線程了岩灭,一看主隊列,就等著主線程執(zhí)行完來執(zhí)行里面代碼       
dispatch_sync(dispatch_get_main_queue(), ^{            
NSLog(@"hello %d  %@",i,[NSThread currentThread]);        
});    }    
NSLog(@"end");
}
  • 解決死鎖的問題柱恤,全局隊列異步執(zhí)行找爱,里面嵌套主隊列同步執(zhí)行
    當(dāng)我們將主隊列同步執(zhí)行任務(wù)放到子線程去執(zhí)行车摄,就不會出現(xiàn)死鎖。
    由于將主隊列同步放到了子線程中執(zhí)行变屁,主隊列同步任務(wù)無法阻塞主線程執(zhí)行代碼意狠,因此主線程可以將主線程上的代碼執(zhí)行完畢。當(dāng)主線程執(zhí)行完畢之后闷板,就會執(zhí)行主隊列里面的任務(wù)。

先執(zhí)行begin和end,再順序執(zhí)行全局隊列異步執(zhí)行(第二個線程里執(zhí)行)

- (void)demo3 {
    NSLog(@"begin");
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (int i = 0; i < 10; i++) {
            dispatch_sync(dispatch_get_main_queue(), ^{                
            NSLog(@"hello %d  %@",i,[NSThread currentThread]);            });        }
    });
    NSLog(@"end");
}
  • 主隊列鹏漆,異步執(zhí)行
    執(zhí)行到異步主隊列后创泄,里面代碼異步執(zhí)行后釋放(不創(chuàng)建新線程)鞠抑,主線程執(zhí)行完畢后回來調(diào)用主隊列中代碼

  • 開不開線程? 主隊列,就算是異步執(zhí)行秒梳,也不會開線程箕速。

  • 順序執(zhí)行還是亂序執(zhí)行? 順序執(zhí)行。

  • 先把主線程上的代碼執(zhí)行完畢兴垦,才會執(zhí)行添加到主隊列里面的任務(wù)探越。

  • 主隊列和串行隊列的區(qū)別

  • 串行隊列:必須等待一個任務(wù)執(zhí)行完畢窑业,才會調(diào)度下一個任務(wù),順序執(zhí)行代碼节槐。

  • 主隊列:如果主線程上有代碼執(zhí)行拐纱,主隊列就不調(diào)度任務(wù)秸架,跳過主隊列代碼執(zhí)行主線程代碼,完畢后再執(zhí)行主隊列內(nèi)代碼蚂子。

4. 全局隊列:
  • 全局隊列是系統(tǒng)提供的,無需自己創(chuàng)建蒂破,可以直接通過dispatch_get_global_queue(long identifier, unsigned long flags);函數(shù)來獲取别渔。

  • 全局隊列的工作特性跟并發(fā)隊列一致。 實際上喇伯,全局隊列就是系統(tǒng)為了方便程序員拨与,專門提供的一種特殊的并發(fā)隊列买喧。

  • 全局隊列和并發(fā)隊列的區(qū)別

  • 全局隊列:沒有名稱,無論ARC還是MRC都不需要考慮內(nèi)存釋放秋度,日常開發(fā)钱床,建議使用全局隊列

  • 并發(fā)隊列:
    (1)有名稱,如果在MRC開發(fā)中事期,需要使用dispatch_release來釋放相應(yīng)的對象
    (2)dispatch_barrier 必須使用自定義的并發(fā)隊列
    (3)開發(fā)第三方框架兽泣,建議使用并發(fā)隊列

  • 第一個參數(shù): identifier
    iOS7.0胁孙,表示的是優(yōu)先級:

    DISPATCH_QUEUE_PRIORITY_HIGH = 2; 高優(yōu)先級
    DISPATCH_QUEUE_PRIORITY_DEFAULT = 0; 默認(rèn)優(yōu)先級
    DISPATCH_QUEUE_PRIORITY_LOW = -2; 低優(yōu)先級
    DISPATCH_QUEUE_PRIORITY_BACKGROUND = INT16_MIN; 后臺優(yōu)先級
iOS8.0開始涮较,推薦使用服務(wù)質(zhì)量(QOS):
    QOS_CLASS_USER_INTERACTIVE  = 0x21; 用戶交互
    QOS_CLASS_USER_INITIATED    = 0x19; 用戶期望
    QOS_CLASS_DEFAULT         = 0x15; 默認(rèn)
    QOS_CLASS_UTILITY         = 0x11; 實用工具
    QOS_CLASS_BACKGROUND      = 0x09; 后臺
    QOS_CLASS_UNSPECIFIED       = 0x00; 未指定

通過對比可知: 第一個參數(shù)傳入0,可以同時適配iOS7及iOS7以后的版本候齿。
服務(wù)質(zhì)量和優(yōu)先級是一一對應(yīng)的:

    DISPATCH_QUEUE_PRIORITY_HIGH:          QOS_CLASS_USER_INITIATED
    DISPATCH_QUEUE_PRIORITY_DEFAULT:      QOS_CLASS_DEFAULT
    DISPATCH_QUEUE_PRIORITY_LOW:           QOS_CLASS_UTILITY
    DISPATCH_QUEUE_PRIORITY_BACKGROUND:   QOS_CLASS_BACKGROUND
  • 第二個參數(shù): flags 為未來保留使用的,始終傳入0周霉。

方法:

  1. 獲取系統(tǒng)隊列
    (1)獲取主隊列(一種串行隊列) dispatch_queue_t 類型
dispatch_get_main_queue()

(2)獲取全局隊列(一種并發(fā)隊列) dispatch_queue_t 類型

dispatch_get_global_queue(0, 0)
dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags);
  1. 自定義創(chuàng)建一個串行俱箱、并發(fā)隊列
    參數(shù)1:隊列名
    參數(shù)2:隊列類型灭必,串行還是并發(fā)隊列
    串行隊列:DISPATCH_QUEUE_SERIAL 或 NULL
    并發(fā)隊列:DISPATCH_QUEUE_CONCURRENT
dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);

例子:創(chuàng)建一個串行隊列

dispatch_queue_t queue = dispatch_queue_create("hm", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue = dispatch_queue_create("itheima", NULL);
  1. 創(chuàng)建任務(wù)
typedef void (^dispatch_block_t)(void);

例子:

dispatch_block_t task = ^{        
NSLog(@"hello %@",[NSThread currentThread]);
};
  1. 將任務(wù)添加到隊列(參數(shù)1:隊列 參數(shù)2:任務(wù))
  • 同步函數(shù)厂财,任務(wù)會在當(dāng)前線程執(zhí)行峡懈,因為同步函數(shù)不具備開新線程的能力肪康。
    同步:你必須把我的代碼執(zhí)行完你再走,一定要執(zhí)行完同步里的代碼再執(zhí)行下面的代碼
void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
  • 異步函數(shù)谒撼,任務(wù)會在子線程執(zhí)行灶伊,因為異步函數(shù)具備開新線程的能力。
    異步:你先走執(zhí)行我下面的代碼,我找人移盆、找線程去執(zhí)行我里面的代碼
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

例子:將任務(wù)同步添加到隊列和將任務(wù)異步添加到隊列

(1)dispatch_sync(queue, task);
(2)dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"hello %@",[NSThread currentThread]);
    });
  1. Barrier阻塞
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
  • 概念:會阻塞dispatch_barrier_async代碼里的任務(wù)咒循,讓其它任務(wù)先執(zhí)行完畢后再去執(zhí)行里面的代碼
  • 用處:
    (1)適合于大規(guī)模的 I/O 操作
    (2)當(dāng)訪問數(shù)據(jù)庫或文件的時候叙甸,更新數(shù)據(jù)的時候不能和其他更新或讀取的操作在同一時間執(zhí)行,可以使用調(diào)度組不過有點復(fù)雜便脊。可以使用dispatch_barrier_async解決遂赠。
  • 例子:執(zhí)行結(jié)果十個圖片下載完成先執(zhí)行完畢晌杰,再執(zhí)行十個保存圖片
_queue = dispatch_queue_create("110", DISPATCH_QUEUE_CONCURRENT);
for (int i = 1; i<=10; i++) {    
[self downloadImage:i];}
-(void)downloadImage:(int)index {
    dispatch_async(_queue, ^{        //模擬下載圖片        
    NSString *fileName = [NSString stringWithFormat:@"%02d.jpg",index % 10 + 1];        
    NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];
    UIImage *img = [UIImage imageWithContentsOfFile:path];
        //等待隊列中所有的任務(wù)執(zhí)行完成(十個圖片下載完成任務(wù))肋演,才會執(zhí)行barrier中的代碼
        dispatch_barrier_async(_queue, ^{            
[self.photoList addObject:img];            
NSLog(@"保存圖片 %@   %@",fileName,[NSThread currentThread]);
        }); 
        NSLog(@"圖片下載完成 %@  %@",fileName,[NSThread currentThread]);
    }); 
}
  1. 延時操作
void dispatch_after(dispatch_time_t when,
dispatch_queue_t queue,
dispatch_block_t block);
  • 參數(shù)1:dispatch_time_t when
    多少納秒之后執(zhí)行
dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta);
#define DISPATCH_TIME_NOW (0ull)
#define DISPATCH_TIME_FOREVER (~0ull)
#define NSEC_PER_SEC 1000000000ull#define NSEC_PER_MSEC 1000000ull#define USEC_PER_SEC 1000000ull
#define NSEC_PER_USEC 1000ull
  • 參數(shù)2:dispatch_queue_t queue
    任務(wù)添加到哪個隊列
  • 參數(shù)3:dispatch_block_t block
    要執(zhí)行的任務(wù)
    用法:延遲1秒后在主隊列執(zhí)行一個任務(wù)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
  1. 一次性執(zhí)行
  • 注意:一次性執(zhí)行是線程安全的爹殊,只在當(dāng)前線程上一次性執(zhí)行
  • 步驟:
    (1)創(chuàng)建一個靜態(tài)的 dispatch_once_t 變量梗夸;
    (2) dispatch_once(變量名, ^{
    //一次性執(zhí)行語句
    });
  • 原理:判斷靜態(tài)的全局變量的值,默認(rèn)是0辛块,執(zhí)行完成后變?yōu)?1
    dispatch_once內(nèi)部會判斷變量的值铅碍,如果是0才執(zhí)行
  • 用處:可以使用一次性執(zhí)行創(chuàng)建單例對象,效率比互斥鎖高
  • 例子:
for (int i = 0; i<20000; i++) {    
static dispatch_once_t onceToken;    
dispatch_once(&onceToken, ^{        
NSLog(@"hello %@",[NSThread currentThread]);    });
}
  1. 調(diào)度組
  • 用處:有時候需要在多個異步任務(wù)都執(zhí)行完成之后繼續(xù)做某些事情尘盼,比如下載歌曲悔叽,等所有的歌曲都下載完畢之后再轉(zhuǎn)到主線程提示用戶
  • 步驟:
    (1)創(chuàng)建隊列
    (2)創(chuàng)建調(diào)度組
dispatch_group_t group = dispatch_group_create();

(3)監(jiān)聽調(diào)度組內(nèi)隊列任務(wù)是否執(zhí)行完畢:把任務(wù)添加到隊列中爵嗅,隊列添加到調(diào)度組中,隊列任務(wù)執(zhí)行完畢通知調(diào)度組

void dispatch_group_async(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);

(4)接收到調(diào)度組執(zhí)行完畢的通知后睹晒,執(zhí)行其它任務(wù)

void dispatch_group_notify(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);

例子:

//創(chuàng)建組
dispatch_group_t group = dispatch_group_create();
//隊列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//下載第一首歌曲
dispatch_group_async(group, queue, ^{
    NSLog(@"正在下載第一個歌曲");
});
//下載第二首歌曲
dispatch_group_async(group, queue, ^{    
NSLog(@"正在下載第二個歌曲");
    [NSThread sleepForTimeInterval:2.0];
});//下載第三首歌曲dispatch_group_async(group, queue, ^{    NSLog(@"正在下載第三個歌曲");
});
//當(dāng)三個異步任務(wù)都執(zhí)行完成伪很,才執(zhí)行dispatch_group_notify(group, dispatch_get_main_queue(), ^{    NSLog(@"over %@",[NSThread currentThread]);
});
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末锉试,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子拖云,更是在濱河造成了極大的恐慌,老刑警劉巖乏苦,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件汇荐,死亡現(xiàn)場離奇詭異盆繁,居然都是意外死亡,警方通過查閱死者的電腦和手機繁疤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸣哀,“玉大人吞彤,你說我怎么就攤上這事∧痈幔” “怎么了埋嵌?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵雹嗦,是天一觀的道長。 經(jīng)常有香客問我锭环,道長泊藕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任蛾茉,我火速辦了婚禮景醇,結(jié)果婚禮上三痰,老公的妹妹穿的比我還像新娘。我一直安慰自己散劫,他們只是感情好获搏,可當(dāng)我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布常熙。 她就那樣靜靜地躺著,像睡著了一般仿贬。 火紅的嫁衣襯著肌膚如雪墓贿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天队伟,我揣著相機與錄音嗜侮,去河邊找鬼啥容。 笑死,一個胖子當(dāng)著我的面吹牛宜猜,可吹牛的內(nèi)容都是我干的硝逢。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼柴罐,長吁一口氣:“原來是場噩夢啊……” “哼憨奸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起似芝,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤党瓮,失蹤者是張志新(化名)和其女友劉穎盐类,沒想到半個月后在跳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡瓷翻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了韭脊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片单旁。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡象浑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出篓吁,到底是詐尸還是另有隱情蚪拦,我是刑警寧澤冻押,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布洛巢,位于F島的核電站次兆,受9級特大地震影響芥炭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜米苹,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一蘸嘶、第九天 我趴在偏房一處隱蔽的房頂上張望陪汽。 院中可真熱鬧,春花似錦况增、人聲如沸训挡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽颊艳。三九已至忘分,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間重斑,已是汗流浹背肯骇。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留寒矿,地道東北人符相。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓啊终,卻偏偏與公主長得像,于是被迫代替她去往敵國和親趟脂。 傳聞我的和親對象是個殘疾皇子例衍,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,955評論 2 355

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