GCD

同步、異步免绿、串行唧席、并行的概念

同步/異步:指的是能否開啟新的線程,同步不能開啟新的線程嘲驾,異步可以淌哟。
串行/并行:指的是任務(wù)的執(zhí)行方式,串行是指有多個(gè)任務(wù)時(shí)距淫,各個(gè)任務(wù)按順序執(zhí)行绞绒,完成一個(gè)之后才能進(jìn)行下一個(gè)。并行指的是多個(gè)任務(wù)可以同時(shí)執(zhí)行榕暇。
異步是多個(gè)任務(wù)并行的前提條件。

名稱 特點(diǎn)
同步執(zhí)行 不具備開啟新線程的能力,
任務(wù)創(chuàng)建后要執(zhí)行完才能繼續(xù)往下走
異步執(zhí)行 具備開啟新線程的能力彤枢,
任務(wù)創(chuàng)建后可以先繞過狰晚,然后再執(zhí)行
串行隊(duì)列 隊(duì)列中的任務(wù)要按順序執(zhí)行
并行隊(duì)列 隊(duì)列中的任務(wù)同時(shí)執(zhí)行

線程、任務(wù)缴啡、隊(duì)列的概念

名稱 特點(diǎn)
線程 程序執(zhí)行任務(wù)的最小調(diào)度單位
任務(wù) 說白了就是一段代碼壁晒,在GCD中,
任務(wù)就是Block中要執(zhí)行的內(nèi)容
隊(duì)列 用來存放“任務(wù)”的一個(gè)數(shù)組

所有組合

并行隊(duì)列 串行隊(duì)列 主隊(duì)列
異步執(zhí)行 開啟多個(gè)新線程业栅,任務(wù)同時(shí)執(zhí)行 開啟一個(gè)新線程秒咐,任務(wù)按順序執(zhí)行 不開啟新的線程,任務(wù)按順序執(zhí)行
同步執(zhí)行 不開啟新線程碘裕,任務(wù)按順序執(zhí)行 不開啟新線程携取,任務(wù)按順序執(zhí)行 死鎖

死鎖:兩個(gè)(多個(gè))線程都要等待對(duì)方完成某個(gè)操作才能進(jìn)行下一步,這時(shí)就會(huì)發(fā)生死鎖帮孔。

代碼編程實(shí)現(xiàn)

獲取隊(duì)列(三種方式)

1雷滋、自定義隊(duì)列

//自定義并行隊(duì)列
-(dispatch_queue_t)createConcurrentQueue{
    dispatch_queue_t queue = dispatch_queue_create("LN_Concurrent", DISPATCH_QUEUE_CONCURRENT);
    return queue;
}

//自定義串行隊(duì)列
-(dispatch_queue_t)createSerialQueue{
    dispatch_queue_t queue = dispatch_queue_create("LN_Serial", DISPATCH_QUEUE_SERIAL);
    return queue;
}

2、主線程串行隊(duì)列

//獲取主線程串行隊(duì)列
-(dispatch_queue_t)getMainSerialQueue{
    dispatch_queue_t queue = dispatch_get_main_queue();
    return queue;
}

3文兢、全局并發(fā)隊(duì)列

//獲取全局并發(fā)隊(duì)列
-(dispatch_queue_t)getGlobalConcurrentQueue{
    /*
     * 第一個(gè)參數(shù):優(yōu)先級(jí)別
     DISPATCH_QUEUE_PRIORITY_HIGH
     DISPATCH_QUEUE_PRIORITY_DEFAULT
     DISPATCH_QUEUE_PRIORITY_LOW
     DISPATCH_QUEUE_PRIORITY_GACKGROUND
     */
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    return queue;
}

為隊(duì)列添加任務(wù)(兩種方式)

1晤斩、異步添加任務(wù)

//異步
-(void)addTaskWithAsyncInQueue:(dispatch_queue_t)queue{
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)1開始");
        sleep(5);
        NSLog(@"任務(wù)1結(jié)束");
    });
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)2開始");
        sleep(2);
        NSLog(@"任務(wù)2結(jié)束");
    });
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)3開始");
        sleep(1);
        NSLog(@"任務(wù)3結(jié)束");
    });
}

2、同步添加任務(wù)

-(void)addTaskWithSyncInQueue:(dispatch_queue_t)queue{
    dispatch_sync(queue, ^{
        NSLog(@"任務(wù)1開始");
        sleep(5);
        NSLog(@"任務(wù)1結(jié)束");
    });
    dispatch_sync(queue, ^{
        NSLog(@"任務(wù)2開始");
        sleep(2);
        NSLog(@"任務(wù)2結(jié)束");
    });
    dispatch_sync(queue, ^{
        NSLog(@"任務(wù)3開始");
        sleep(1);
        NSLog(@"任務(wù)3結(jié)束");
    });
}

組合執(zhí)行

(一)異步+并行

//異步+并行
-(void)lnAsyncConcurrent{
    dispatch_queue_t queue = [self createConcurrentQueue];
    NSLog(@"======start=====");
    [self addTaskWithAsyncInQueue:queue];
    NSLog(@"======end=====");
}

執(zhí)行輸出結(jié)果:

2018-04-17 14:28:03.797234+0800 ThreadProject[1708:124655] ======start=====
2018-04-17 14:28:03.797451+0800 ThreadProject[1708:124655] ======end=====
2018-04-17 14:28:03.797510+0800 ThreadProject[1708:124714] 任務(wù)1開始
2018-04-17 14:28:03.797512+0800 ThreadProject[1708:124711] 任務(wù)3開始
2018-04-17 14:28:03.797512+0800 ThreadProject[1708:124713] 任務(wù)2開始
2018-04-17 14:28:04.802118+0800 ThreadProject[1708:124711] 任務(wù)3結(jié)束
2018-04-17 14:28:05.799360+0800 ThreadProject[1708:124713] 任務(wù)2結(jié)束
2018-04-17 14:28:08.801884+0800 ThreadProject[1708:124714] 任務(wù)1結(jié)束

在代碼的任務(wù)3中設(shè)置斷點(diǎn)姆坚,查看線程數(shù)


開啟了三個(gè)線程
總結(jié):
- 開了三個(gè)新線程
- 函數(shù)在執(zhí)行時(shí)澳泵,先打印了start和end,再回頭執(zhí)行這三個(gè)任務(wù)
這是異步執(zhí)行的結(jié)果兼呵,異步執(zhí)行會(huì)開啟新線程烹俗,任務(wù)可以先繞過不執(zhí)行,回頭再來執(zhí)行萍程。
- 三個(gè)任務(wù)同時(shí)開始
這是并發(fā)的結(jié)果

(二)異步+串行

//異步+串行
-(void)lnAsyncSerial{
    dispatch_queue_t queue = [self createSerialQueue];
    NSLog(@"======start=====");
    [self addTaskWithAsyncInQueue:queue];
    NSLog(@"======end=====");
}

執(zhí)行輸出結(jié)果:

2018-04-17 15:35:17.971527+0800 ThreadProject[2071:164583] ======start=====
2018-04-17 15:35:17.971778+0800 ThreadProject[2071:164583] ======end=====
2018-04-17 15:35:17.971823+0800 ThreadProject[2071:164636] 任務(wù)1開始
2018-04-17 15:35:22.974270+0800 ThreadProject[2071:164636] 任務(wù)1結(jié)束
2018-04-17 15:35:22.974649+0800 ThreadProject[2071:164636] 任務(wù)2開始
2018-04-17 15:35:24.978868+0800 ThreadProject[2071:164636] 任務(wù)2結(jié)束
2018-04-17 15:35:24.979185+0800 ThreadProject[2071:164636] 任務(wù)3開始
2018-04-17 15:35:25.983574+0800 ThreadProject[2071:164636] 任務(wù)3結(jié)束
開啟了新線程
總結(jié):相比異步+并行幢妄,這個(gè)的任務(wù)執(zhí)行順序是一個(gè)一個(gè)來的,上一個(gè)任務(wù)結(jié)束了才開始下一個(gè)
任務(wù)茫负。
這是串行的結(jié)果

(三)異步+主隊(duì)列

//異步+主隊(duì)列
-(void)lnAsyncMain{
    dispatch_queue_t queue = dispatch_get_main_queue();
    NSLog(@"======start=====");
    [self addTaskWithAsyncInQueue:queue];
    NSLog(@"======end=====");
}

執(zhí)行輸出結(jié)果:

2018-04-17 15:41:25.099769+0800 ThreadProject[2071:164583] ======start=====
2018-04-17 15:41:25.099955+0800 ThreadProject[2071:164583] ======end=====
2018-04-17 15:41:25.101869+0800 ThreadProject[2071:164583] 任務(wù)1開始
2018-04-17 15:41:30.103308+0800 ThreadProject[2071:164583] 任務(wù)1結(jié)束
2018-04-17 15:41:30.103591+0800 ThreadProject[2071:164583] 任務(wù)2開始
2018-04-17 15:41:32.104805+0800 ThreadProject[2071:164583] 任務(wù)2結(jié)束
2018-04-17 15:41:32.105079+0800 ThreadProject[2071:164583] 任務(wù)3開始
2018-04-17 15:42:34.792503+0800 ThreadProject[2071:164583] 任務(wù)3結(jié)束
未開啟新線程
總結(jié):執(zhí)行輸出結(jié)果與異步+串行是一樣的蕉鸳,這是因?yàn)橹麝?duì)列就是一個(gè)串行隊(duì)列。
不同的是:不開啟新的線程忍法,而是在主線程上運(yùn)行潮尝。

(四)同步+并行

//同步+并行
-(void)lnSyncConcurrent{
    dispatch_queue_t queue = [self createConcurrentQueue];
    NSLog(@"======start=====");
    [self addTaskWithSyncInQueue:queue];
    NSLog(@"======end=====");
}

執(zhí)行輸出結(jié)果:

2018-04-17 15:47:48.893351+0800 ThreadProject[2071:164583] ======start=====
2018-04-17 15:47:48.893553+0800 ThreadProject[2071:164583] 任務(wù)1開始
2018-04-17 15:47:53.894956+0800 ThreadProject[2071:164583] 任務(wù)1結(jié)束
2018-04-17 15:47:53.895313+0800 ThreadProject[2071:164583] 任務(wù)2開始
2018-04-17 15:47:55.896732+0800 ThreadProject[2071:164583] 任務(wù)2結(jié)束
2018-04-17 15:47:55.897079+0800 ThreadProject[2071:164583] 任務(wù)3開始
2018-04-17 15:47:56.898450+0800 ThreadProject[2071:164583] 任務(wù)3結(jié)束
2018-04-17 15:47:56.898782+0800 ThreadProject[2071:164583] ======end=====
總結(jié):根據(jù)程序代碼從上往下走,不開啟新線程饿序。

(五)同步+串行
輸出結(jié)果與同步+并行是相同的勉失。

總結(jié):
同步+并行與同步+串行的區(qū)別:同步+并行使用嵌套調(diào)用不會(huì)產(chǎn)生死鎖,同步+串行嵌套調(diào)用會(huì)產(chǎn)生死鎖原探。

(六)同步+主隊(duì)列

//同步+主隊(duì)列
-(void)lnSyncMain{
    dispatch_queue_t queue = dispatch_get_main_queue();
    NSLog(@"======start=====");
    [self addTaskWithSyncInQueue:queue];
    NSLog(@"======end=====");
}

死鎖

死鎖產(chǎn)生原因:主隊(duì)列上先有了一個(gè)lnSyncMain這個(gè)任務(wù)乱凿,在lnSyncMain方法中又在主隊(duì)列上添加了任務(wù)顽素。由于是串行,先要lnSyncMain這個(gè)任務(wù)完成徒蟆,才執(zhí)行后添加的任務(wù)胁出。但是lnSyncMain這個(gè)任務(wù)的完成又依賴于添加的block。所以就出現(xiàn)了循環(huán)等待段审,導(dǎo)致死鎖全蝶。

死鎖測試:

//嵌套 同步+并行 (不會(huì)產(chǎn)生死鎖)
-(void)testForLock{
    dispatch_queue_t queue = [self createConcurrentQueue];
    NSLog(@"======start=====");
    dispatch_sync(queue, ^{
        NSLog(@"任務(wù)1開始");
        dispatch_sync(queue, ^{
            NSLog(@"任務(wù)2開始");
            NSLog(@"任務(wù)2結(jié)束");
        });
        NSLog(@"任務(wù)1結(jié)束");
    });
    NSLog(@"======end=====");
}

//嵌套 同步+串行(會(huì)產(chǎn)生死鎖)
-(void)testForLockTwo{
    dispatch_queue_t queue = [self createSerialQueue];
    NSLog(@"======start=====");
    dispatch_sync(queue, ^{
        NSLog(@"任務(wù)1開始");
        dispatch_sync(queue, ^{
            NSLog(@"任務(wù)2開始");
            NSLog(@"任務(wù)2結(jié)束");
        });
        NSLog(@"任務(wù)1結(jié)束");
    });
    NSLog(@"======end=====");
}

注意:不要嵌套使用同步執(zhí)行的串行隊(duì)列任務(wù)

GCD其他方法

  • dispatch_once
    保證在app運(yùn)行期間,block中的代碼只執(zhí)行一次寺枉。常用于單例的初始化抑淫。
  • dispatch_barrier_async
    1、在并行隊(duì)列中姥闪,等待在dispatch_barrier_async之前加入的任務(wù)全部執(zhí)行完成之后(這些任務(wù)是并發(fā)執(zhí)行的)
    2始苇、再執(zhí)行dispatch_barrier_async中的任務(wù)
    3、dispatch_barrier_async中的任務(wù)執(zhí)行完成之后甘畅,再去執(zhí)行在dispatch_barrier_async之后加入到隊(duì)列中的任務(wù)(這些任務(wù)是并發(fā)執(zhí)行的)埂蕊。
    使用場景:多讀單寫
//異步柵欄(多讀單寫場景)
-(void)lnAsyncBarrier{
    dispatch_queue_t queue = [self createConcurrentQueue];
    NSLog(@"======start=====");
    [self addTaskWithAsyncInQueue:queue];
    /*
     *1、等待dispatch_barrier_async之前的任務(wù)全部執(zhí)行完
     *2疏唾、執(zhí)行dispatch_barrier_async的任務(wù)
     *3蓄氧、執(zhí)行dispatch_barrier_async之后的任務(wù)
     */
    dispatch_barrier_async(queue, ^{
        NSLog(@"柵欄方法");
    });
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)5開始");
        sleep(3);
        NSLog(@"任務(wù)5結(jié)束");
    });
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)6開始");
        sleep(1);
        NSLog(@"任務(wù)6結(jié)束");
    });
    NSLog(@"======end=====");
}
  • dispatch_group_notify
    結(jié)合dispatch_group_t一起使用,等待組里的任務(wù)全部完成后槐脏,調(diào)用dispatch_group_notify的block
    使用場景:同時(shí)下載多個(gè)圖片喉童,所有圖片下載完成之后去更新UI(回到主線程)
//group queue
-(void)lnGroupQueue{
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = [self createConcurrentQueue];
    //假設(shè)這個(gè)數(shù)組用于存放圖片的下載地址
    NSArray *arrayURLs = @[@"圖片下載地址1",@"圖片下載地址2",@"圖片下載地址3"];
    for(NSString *url in arrayURLs){
        dispatch_group_async(group, queue, ^{
            //根據(jù)url去下載圖片
            
            NSLog(@"%@",url);
        });
    }
    //主線程上操作
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 當(dāng)添加到組中的所有任務(wù)執(zhí)行完成之后會(huì)調(diào)用該Block
        NSLog(@"所有圖片已全部下載完成");
    });
}

項(xiàng)目源碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市顿天,隨后出現(xiàn)的幾起案子堂氯,更是在濱河造成了極大的恐慌,老刑警劉巖牌废,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咽白,死亡現(xiàn)場離奇詭異,居然都是意外死亡鸟缕,警方通過查閱死者的電腦和手機(jī)晶框,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來懂从,“玉大人授段,你說我怎么就攤上這事》Γ” “怎么了侵贵?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缘薛。 經(jīng)常有香客問我窍育,道長卡睦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任蔫骂,我火速辦了婚禮么翰,結(jié)果婚禮上牺汤,老公的妹妹穿的比我還像新娘辽旋。我一直安慰自己,他們只是感情好檐迟,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布补胚。 她就那樣靜靜地躺著,像睡著了一般追迟。 火紅的嫁衣襯著肌膚如雪溶其。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天敦间,我揣著相機(jī)與錄音瓶逃,去河邊找鬼。 笑死廓块,一個(gè)胖子當(dāng)著我的面吹牛厢绝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播带猴,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼昔汉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了拴清?” 一聲冷哼從身側(cè)響起靶病,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎口予,沒想到半個(gè)月后娄周,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沪停,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年煤辨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牙甫。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡掷酗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出窟哺,到底是詐尸還是另有隱情泻轰,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布且轨,位于F島的核電站浮声,受9級(jí)特大地震影響虚婿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜泳挥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一然痊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧屉符,春花似錦剧浸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至吨艇,卻和暖如春躬它,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背东涡。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國打工冯吓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人疮跑。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓组贺,卻偏偏與公主長得像,于是被迫代替她去往敵國和親祸挪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子锣披,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

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