GCD 知識總結(jié)

(持續(xù)更新中~)

GCD

GCD中兩個非常重要的概念: 任務(wù)隊列

任務(wù)分為同步執(zhí)行sync異步執(zhí)行async, 同步和異步的區(qū)別在于是否會阻塞當(dāng)前線程, 其實(shí)在GCD中一個任務(wù)就是一個block中的代碼.

隊列分為串行隊列并行隊列,主隊列dispatch_get_main_queue( )是串行隊列.

我們可以使用dispatch_queue_create來創(chuàng)建新的隊列,

//串行隊列
  dispatch_queue_t queue = dispatch_queue_create("testQueue", NULL);
  dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL);
  //并行隊列
  dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);

下面是我自己總結(jié)的:

/ 同步執(zhí)行sync 異步執(zhí)行async
串行隊列 當(dāng)前線程,一個一個執(zhí)行 其他一個線程, 一個一個執(zhí)行
并行隊列 當(dāng)前線程,一個一個執(zhí)行 其他一個或者多個線程(取決于任務(wù)數(shù)), 同時執(zhí)行

通過代碼來驗(yàn)證一下:

  1. 創(chuàng)建一個串行的隊列添加4個同步執(zhí)行的任務(wù)
//串行隊列
  dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL); 
  dispatch_sync(queue, ^{
      NSLog(@"111");
      NSLog(@"111中 %@",[NSThread currentThread]);
  });
  dispatch_sync(queue, ^{
      NSLog(@"222");
      NSLog(@"222中 %@",[NSThread currentThread]);
  });
  dispatch_sync(queue, ^{
      NSLog(@"333");
      NSLog(@"333中 %@",[NSThread currentThread]);
  });
  dispatch_sync(queue, ^{
      NSLog(@"444");
      NSLog(@"444中 %@",[NSThread currentThread]);
  });

打印結(jié)果:

2018-03-09 15:34:26.474786+0800 Learning[3385:281473] 111
2018-03-09 15:34:26.475053+0800 Learning[3385:281473] 111中 <NSThread: 0x60000007a2c0>{number = 1, name = main}
2018-03-09 15:34:26.475165+0800 Learning[3385:281473] 222
2018-03-09 15:34:26.475369+0800 Learning[3385:281473] 222中 <NSThread: 0x60000007a2c0>{number = 1, name = main}
2018-03-09 15:34:26.475568+0800 Learning[3385:281473] 333
2018-03-09 15:34:26.476057+0800 Learning[3385:281473] 333中 <NSThread: 0x60000007a2c0>{number = 1, name = main}
2018-03-09 15:34:26.476249+0800 Learning[3385:281473] 444
2018-03-09 15:34:26.476351+0800 Learning[3385:281473] 444中 <NSThread: 0x60000007a2c0>{number = 1, name = main}
  1. 同步dispatch_sync改為異步dispatch_async
    打印結(jié)果依然是有序, 但是開啟了一個子線程
2018-03-09 16:23:47.634329+0800 Learning[3830:331376] 111
2018-03-09 16:23:47.634739+0800 Learning[3830:331376] 111中 <NSThread: 0x604000274dc0>{number = 4, name = (null)}
2018-03-09 16:23:47.634911+0800 Learning[3830:331376] 222
2018-03-09 16:23:47.635262+0800 Learning[3830:331376] 222中 <NSThread: 0x604000274dc0>{number = 4, name = (null)}
2018-03-09 16:23:47.636508+0800 Learning[3830:331376] 333
2018-03-09 16:23:47.637206+0800 Learning[3830:331376] 333中 <NSThread: 0x604000274dc0>{number = 4, name = (null)}
2018-03-09 16:23:47.637413+0800 Learning[3830:331376] 444
2018-03-09 16:23:47.637680+0800 Learning[3830:331376] 444中 <NSThread: 0x604000274dc0>{number = 4, name = (null)}
  1. 如果把新建的隊列改為并行隊列, 同步執(zhí)行
  //并行隊列
dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
      NSLog(@"111");
      NSLog(@"111中 %@",[NSThread currentThread]);
  });
  dispatch_sync(queue, ^{
      NSLog(@"222");
      NSLog(@"222中 %@",[NSThread currentThread]);
  });
  dispatch_sync(queue, ^{
      NSLog(@"333");
      NSLog(@"333中 %@",[NSThread currentThread]);
  });
  dispatch_sync(queue, ^{
      NSLog(@"444");
      NSLog(@"444中 %@",[NSThread currentThread]);
  });

結(jié)果

 2018-03-09 16:31:50.498599+0800 Learning[3938:341075] 111
2018-03-09 16:31:50.498836+0800 Learning[3938:341075] 111中 <NSThread: 0x600000063d80>{number = 1, name = main}
2018-03-09 16:31:50.498946+0800 Learning[3938:341075] 222
2018-03-09 16:31:50.499101+0800 Learning[3938:341075] 222中 <NSThread: 0x600000063d80>{number = 1, name = main}
2018-03-09 16:31:50.499227+0800 Learning[3938:341075] 333
2018-03-09 16:31:50.499353+0800 Learning[3938:341075] 333中 <NSThread: 0x600000063d80>{number = 1, name = main}
2018-03-09 16:31:50.499461+0800 Learning[3938:341075] 444
2018-03-09 16:31:50.499609+0800 Learning[3938:341075] 444中 <NSThread: 0x600000063d80>{number = 1, name = main}
  1. 把上面代碼中的同步改為異步, 即并行隊列, 異步執(zhí)行
2018-03-09 16:34:13.089611+0800 Learning[3982:344351] 222
2018-03-09 16:34:13.089612+0800 Learning[3982:344350] 333
2018-03-09 16:34:13.089612+0800 Learning[3982:344349] 111
2018-03-09 16:34:13.089639+0800 Learning[3982:344352] 444
2018-03-09 16:34:13.089997+0800 Learning[3982:344350] 333中 <NSThread: 0x60000027d680>{number = 3, name = (null)}
2018-03-09 16:34:13.089997+0800 Learning[3982:344349] 111中 <NSThread: 0x604000271800>{number = 4, name = (null)}
2018-03-09 16:34:13.090004+0800 Learning[3982:344352] 444中 <NSThread: 0x604000271440>{number = 6, name = (null)}
2018-03-09 16:34:13.090031+0800 Learning[3982:344351] 222中 <NSThread: 0x604000271480>{number = 5, name = (null)}

從結(jié)果中可以看得出, 開啟了四個不同的現(xiàn)成來執(zhí)行四個任務(wù).


注意
在同步+串行的時候會有一個特殊的情況, 上面也提到了, 主隊列也是一個串行隊列, 如果當(dāng)前在主線程中且把任務(wù)加到主隊列中會如何呢 ?

NSLog(@"---前 %@",[NSThread currentThread]);
dispatch_sync(dispatch_get_main_queue(), ^{//dispatch_sync
    NSLog(@"---中 %@",[NSThread currentThread]);
});
NSLog(@"---后 %@",[NSThread currentThread]);

運(yùn)行結(jié)果形成死鎖.
思考: 同樣都是串行隊列, 為什么任務(wù)添加到主隊列會造成死鎖現(xiàn)象, 新建一個串行隊列則可以正常執(zhí)行 ?
解釋: 在執(zhí)行完第一個NSLog的時候, 遇到dispatch_sync, 會暫時阻塞主線程. 那么我們想一下, 在阻塞之前, 主線程正在執(zhí)行的是主隊列中的哪一個任務(wù)呢? 用我們實(shí)際的例子來解釋, 我們再寫上面的代碼的時候, 這部分的代碼是包裹在一個另方法中的, 就像我自己寫的demo, 這段代碼就寫在viewDidLoad里面, 因?yàn)関iewDidLoad這些任務(wù)也是添加在主隊列中的, 所以說阻塞主線程時, 主線程應(yīng)該是處理的主隊中 viewDidLoad這個任務(wù).
我們調(diào)用了dispatch_sync后向主隊列中添加了新的任務(wù), 也就是dispatch_sync后面block中的代碼. 但是根據(jù)隊列的FIFO規(guī)則, 新添加的block中的任務(wù), 肯定是要排在主隊列的最后.
因?yàn)槭鞘褂玫膁ispatch_sync同步, 所以說必須要等執(zhí)行dispatch_sync的block中的代碼才會返回, 才會繼續(xù)執(zhí)行ViewDidLoad中dispatch_sync這個方法下面的方法, 也就是代碼中的第三個NSLog, 但是block中的任務(wù)被放在了主隊列的底部, 他是不可能在viewDidLoad這個任務(wù)還沒完成的時候就執(zhí)行到的, 所以就形成了兩個任務(wù)互相等待的情況, 也就是形成了死鎖.
所以說這樣看來,GCD形成死鎖的原因應(yīng)該是是隊列阻塞,而不是線程阻塞.

dispatch_group

解決先執(zhí)行A和B, AB都執(zhí)行完再執(zhí)行C的這種情況.

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_group_async(group, queue, ^{
    // 1
    sleep(5);
    NSLog(@"task 1");
});

dispatch_group_async(group, queue, ^{
    //2
    NSLog(@"task 2");
});

dispatch_group_notify(group, queue, ^{
        NSLog(@"done ");
    });

或者

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

dispatch_group_enter(group);
dispatch_async(queue, ^{
    NSLog(@"11");
    dispatch_group_leave(group);
});

dispatch_group_enter(group);
dispatch_async(queue, ^{
    sleep(7);
    NSLog(@"22");
dispatch_group_leave(group);
});

dispatch_group_enter(group);
dispatch_async(queue, ^{
    NSLog(@"33");
    dispatch_group_leave(group);
});

dispatch_group_notify(group, queue, ^{
    NSLog(@"done");
});

結(jié)果都是先執(zhí)行完上面的在打印這個done, 這個不需要解釋了.

有時候可能會碰到dispatch_group_wait, 注意一下, 因?yàn)樗麜枞€程, 所以不能放在主線程里面執(zhí)行, 等待的時間是以group中任務(wù)開始執(zhí)行的時間算起.

dispatch_barrier_async

多線程最常見的問題就是讀寫涯塔,比如數(shù)據(jù)庫讀寫买乃,文件讀寫饭弓,讀取是共享的,寫是互斥.

使用Concurrent Dispatch Queue和dispatch_barrier_basync函數(shù)可實(shí)現(xiàn)高效率的數(shù)據(jù)庫訪問和文件訪問盲憎。
如果允許多個線程進(jìn)行讀操作,當(dāng)寫文件時,阻止隊列中所有其他的線程進(jìn)入凄敢,直到文件寫完成.

在讀取文件時, 直接使用dispatch_async異步讀取, 當(dāng)要寫文件是, 使用dispatch_barrier_async. 用代碼看一下dispatch_async和dispatch_barrier_async的執(zhí)行順序.

 dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"task 1");
    });

    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"task 2");
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"barrier -- ");
        sleep(3);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"task 3");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"task 4");
    });
    
    NSLog(@" end ");

結(jié)果是先打印完task1和2, 在打印完barrier, 最后打印task3和4, 同理到文件讀寫中, 文件讀取直接用dispatch_async, 寫入使用dispatch_barrier_async, 則在寫入文件時, 不管前面有多少操作都會等待前面的操作完成, 而且在寫入的時候, 也沒有其他線程訪問, 從而達(dá)到線程安全.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市湿痢,隨后出現(xiàn)的幾起案子涝缝,更是在濱河造成了極大的恐慌,老刑警劉巖譬重,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拒逮,死亡現(xiàn)場離奇詭異,居然都是意外死亡臀规,警方通過查閱死者的電腦和手機(jī)滩援,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來以现,“玉大人狠怨,你說我怎么就攤上這事∫囟簦” “怎么了佣赖?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長记盒。 經(jīng)常有香客問我憎蛤,道長,這世上最難降的妖魔是什么纪吮? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任俩檬,我火速辦了婚禮,結(jié)果婚禮上碾盟,老公的妹妹穿的比我還像新娘棚辽。我一直安慰自己,他們只是感情好冰肴,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布屈藐。 她就那樣靜靜地躺著,像睡著了一般熙尉。 火紅的嫁衣襯著肌膚如雪联逻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天检痰,我揣著相機(jī)與錄音包归,去河邊找鬼。 笑死铅歼,一個胖子當(dāng)著我的面吹牛公壤,可吹牛的內(nèi)容都是我干的换可。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼境钟,長吁一口氣:“原來是場噩夢啊……” “哼锦担!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起慨削,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎套媚,沒想到半個月后缚态,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡堤瘤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年玫芦,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片本辐。...
    茶點(diǎn)故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡桥帆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出慎皱,到底是詐尸還是另有隱情老虫,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布茫多,位于F島的核電站祈匙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏天揖。R本人自食惡果不足惜夺欲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望今膊。 院中可真熱鬧些阅,春花似錦、人聲如沸斑唬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赖钞。三九已至腰素,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間雪营,已是汗流浹背弓千。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留献起,地道東北人洋访。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓镣陕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親姻政。 傳聞我的和親對象是個殘疾皇子呆抑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評論 2 361

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