1. 并行和并發(fā)有什么區(qū)別?
并行:同一時(shí)刻同時(shí)執(zhí)行不同的任務(wù), 關(guān)鍵點(diǎn)在于同時(shí), 當(dāng)然想達(dá)到這一點(diǎn)至少有兩條線程;
并發(fā):同一時(shí)間段可以執(zhí)行不同的任務(wù), 關(guān)鍵在于 可以執(zhí)行不同的任務(wù), 想做到這一點(diǎn) 一條線程也可以,因?yàn)榭梢栽谝粭l線程中交替執(zhí)行, 多條線程也可以達(dá)到, 不同的線程中執(zhí)行不同的任務(wù);
2. 線程和進(jìn)程?
進(jìn)程:具有獨(dú)立功能的關(guān)于某個(gè)數(shù)據(jù)集合上的第一次運(yùn)行活動(dòng),是系統(tǒng)資源分配和調(diào)度的獨(dú)立單位;
線程:是進(jìn)程的一個(gè)實(shí)體,是CPU調(diào)度和分派的基本代為,它與同一進(jìn)程中的其他線程共享該進(jìn)程中所有資源;
區(qū)別:
1,一個(gè)程序至少一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線程;
2, 進(jìn)程在執(zhí)行過(guò)程中擁有獨(dú)立的內(nèi)存單元,而多個(gè)線程共享內(nèi)存悯舟,從而極大地提高了程序的運(yùn)行效率
3, 線程的劃分尺度小于進(jìn)程担租,使得多線程程序的并發(fā)性高進(jìn)程只能在一個(gè)時(shí)間干一件事,如果想同時(shí)干兩件事或多件事抵怎,進(jìn)程就無(wú)能為力了,同一個(gè)進(jìn)程中的多個(gè)線程之間可以并發(fā)執(zhí)行
收拾下心情,進(jìn)入我們的主角GCD:
1奋救、為什么要用GCD?
- GCD會(huì)自動(dòng)幫我們管理線程的生命周期,它內(nèi)部維護(hù)著一個(gè)線程池;
- 它可以更好的利用多核CPU;
- 使用起來(lái)方便,我們只需要將需要執(zhí)行的任務(wù)放入block就可以了;
2、如何更好的理解任務(wù)和隊(duì)列;
任務(wù):你讓程序干的事情,也就是程序需要執(zhí)行哪些操作;GCD中通常block里面就是一系列的任務(wù);]
隊(duì)列:隊(duì)列是用來(lái)裝任務(wù)的,管理任務(wù)的,不然任務(wù)就亂套了; 它采用FIFO先進(jìn)先出的原則,不管是串行隊(duì)列還是并發(fā)隊(duì)列都是一樣的,注意這里僅僅指的是出隊(duì)列的順序!!!
如圖:任務(wù)4反惕、3尝艘、2、1依次放入隊(duì)列中,那么任務(wù)出去的順序是4姿染、3背亥、2、1;
串行隊(duì)列: 隊(duì)列中的任務(wù)只能一個(gè)個(gè)執(zhí)行,前面一個(gè)沒(méi)有執(zhí)行完,后面一個(gè)不能執(zhí)行;
并發(fā)隊(duì)列: 前面一個(gè)任務(wù)出隊(duì)列開(kāi)始執(zhí)行的時(shí)候,后面一個(gè)就 可以 在新的線程去執(zhí)行了,注意這里是可以,表示一種能力,不一定會(huì)這樣;
后面一個(gè)任務(wù)看到前面一個(gè)在開(kāi)始執(zhí)行狀態(tài)就可以出隊(duì)列,準(zhǔn)備開(kāi)始執(zhí)行了
3悬赏、同步和異步
同步:
- 不開(kāi)啟新的線程就在當(dāng)前線程執(zhí)行,
- 必須等block里面的代碼執(zhí)行完,dispathch才會(huì)返回;執(zhí)行后面的,會(huì)阻塞當(dāng)前線程;
異步:
- 可以開(kāi)啟新的線程,注意是可以 并不一定會(huì),它有這個(gè)能力;
- 不需要等block里面的代碼執(zhí)行完,立即返回,不會(huì)阻塞當(dāng)前線程;
4狡汉、一個(gè)關(guān)于GCD的故事(本故事純屬本人虛構(gòu),如果有不恰當(dāng)?shù)牡胤酱磥?lái)改正)
有兩個(gè)咖啡店,咖啡店1只有一個(gè)窗口賣(mài), 咖啡店2有很多窗口賣(mài), 然后兩個(gè)老師都帶著 排著一條長(zhǎng)隊(duì)的 同學(xué)去買(mǎi)咖啡, 其中一個(gè)老師A告訴她的學(xué)生,你們等前面一個(gè)買(mǎi)好再接著買(mǎi),另一個(gè)老師B告訴她的學(xué)生,你前面的同學(xué)開(kāi)始買(mǎi)選咖啡的時(shí)候,下一個(gè)就可以去找其他窗口開(kāi)始買(mǎi)了,不要等的;
- A老師把她的同學(xué)帶到了咖啡店1:
學(xué)生們是只能等前面買(mǎi)好才能買(mǎi)的,而且就只有一個(gè)窗口, 所以結(jié)果:一個(gè)接一個(gè)按順序買(mǎi)就好
A老師把她的同學(xué)帶到了咖啡店2:
前面一個(gè)同學(xué)正在買(mǎi)的時(shí)候,后面一個(gè)想去找其他窗口開(kāi)始買(mǎi),但是現(xiàn)實(shí)告訴他沒(méi)有其他窗口了,老老實(shí)實(shí)等前面一個(gè)買(mǎi)好吧,,,,,所以結(jié)果:一個(gè)接一個(gè)按順序買(mǎi)就好B老師把她的同學(xué)帶到了咖啡店1:
雖然現(xiàn)實(shí)很好,有好多窗口賣(mài)咖啡,但是 學(xué)生都是乖孩子,有紀(jì)律的,前面一個(gè)買(mǎi)好后面才能買(mǎi),所以就只需要去其他窗口中的一個(gè)窗口買(mǎi)就好了,結(jié)果:一個(gè)接一個(gè)按順序買(mǎi)就好B老師把她的同學(xué)帶到了咖啡店2:
這里的孩紙是最幸運(yùn)的,天時(shí)地利人和; 學(xué)生可以找其他窗口買(mǎi),而且這里恰好就有好多窗口,他們幾乎同時(shí)的在不同的窗戶(hù)買(mǎi)起咖啡來(lái); 結(jié)果:同時(shí)買(mǎi);
5、GCD中的六種組合方式
一. 串行隊(duì)列 + 同步執(zhí)行
- (void) GCDTest1
{
dispatch_queue_t queue = dispatch_queue_create("serialQueue.xj.com", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"1------>>>%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2------>>>%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3------>>>%@",[NSThread currentThread]);
});
NSLog(@"4------>>>%@",[NSThread currentThread]);
}
結(jié)果:
2018-04-01 10:16:17.186574+0800 GCD[55764:12462142] 1------>>><NSThread: 0x604000261c00>{number = 1, name = main}
2018-04-01 10:16:17.186850+0800 GCD[55764:12462142] 2------>>><NSThread: 0x604000261c00>{number = 1, name = main}
2018-04-01 10:16:17.187063+0800 GCD[55764:12462142] 3------>>><NSThread: 0x604000261c00>{number = 1, name = main}
2018-04-01 10:16:17.187228+0800 GCD[55764:12462142] 4------>>><NSThread: 0x604000261c00>{number = 1, name = main}
分析:
這里對(duì)應(yīng)上面故事的第一種情況,由于是同步,不開(kāi)線程,當(dāng)前線程為主線程,所以所有任務(wù)都在主線程中執(zhí)行,又因?yàn)槭谴嘘?duì)列,任務(wù)按順序依次執(zhí)行,而且還是同步,每次需等block內(nèi)執(zhí)行完,所以順序是1-->2--->3--->4;
二. 串行隊(duì)列 + 異步執(zhí)行
- (void) GCDTest2
{
dispatch_queue_t queue = dispatch_queue_create("serialQueue.xj.com", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"1------>>>%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2------>>>%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3------>>>%@",[NSThread currentThread]);
});
NSLog(@"4------>>>%@",[NSThread currentThread]);
}
結(jié)果:
2018-04-01 10:22:41.780449+0800 GCD[56160:12471344] 4------>>><NSThread: 0x604000079940>{number = 1, name = main}
2018-04-01 10:22:41.780478+0800 GCD[56160:12471596] 1------>>><NSThread: 0x60400027f0c0>{number = 3, name = (null)}
2018-04-01 10:22:41.780803+0800 GCD[56160:12471596] 2------>>><NSThread: 0x60400027f0c0>{number = 3, name = (null)}
2018-04-01 10:22:41.781027+0800 GCD[56160:12471596] 3------>>><NSThread: 0x60400027f0c0>{number = 3, name = (null)}
分析:
因?yàn)槭钱惒綀?zhí)行,不會(huì)阻塞當(dāng)前線程,所以先執(zhí)行4,,開(kāi)辟了一條新的線程執(zhí)行1,2,3,然而1,2,3是放到串行隊(duì)列中的,所以依次然順序執(zhí)行1-->2-->3; 這里提一下,既然是異步,為什么不開(kāi)辟多條線程分別執(zhí)行1,2,3,沒(méi)這個(gè)必要,既然已經(jīng)開(kāi)了一天線程,后面的任務(wù)要等前面的執(zhí)行完再執(zhí)行,這個(gè)線程足以讓1,2,3執(zhí)行任務(wù)了,開(kāi)辟線程是消耗資源的;
三. 并發(fā)隊(duì)列 + 同步執(zhí)行
- (void) GCDTest3
{
dispatch_queue_t queue = dispatch_queue_create("xj", DISPATCH_QUEUE_CONCURRENT);;
dispatch_sync(queue, ^{
NSLog(@"1------>>>%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2------>>>%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3------>>>%@",[NSThread currentThread]);
});
NSLog(@"4------>>>%@",[NSThread currentThread]);
}
結(jié)果
2018-04-01 10:30:24.402362+0800 GCD[56597:12479909] 1------>>><NSThread: 0x60400006f580>{number = 1, name = main}
2018-04-01 10:30:24.432775+0800 GCD[56597:12479909] 2------>>><NSThread: 0x60400006f580>{number = 1, name = main}
2018-04-01 10:30:24.439568+0800 GCD[56597:12479909] 3------>>><NSThread: 0x60400006f580>{number = 1, name = main}
2018-04-01 10:30:24.439568+0800 GCD[56160:12471344] 4------>>><NSThread: 0x604000079940>{number = 1, name = main}
分析:由于是同步執(zhí)行,所以不會(huì)另開(kāi)線程,阻塞當(dāng)前線程,所以4是最后執(zhí)行的,雖然是并發(fā)隊(duì)列可以不用等前面的任務(wù)執(zhí)行完,但是就一條線程,不得不等前面的任務(wù)執(zhí)行完才執(zhí)行后面的任務(wù),所以1,2,3依次執(zhí)行;
四. 并發(fā)隊(duì)列 + 異步執(zhí)行
- (void) GCDTest4
{
dispatch_queue_t queue = dispatch_queue_create("xj", DISPATCH_QUEUE_CONCURRENT);;
dispatch_async(queue, ^{
NSLog(@"1------>>>%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2------>>>%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3------>>>%@",[NSThread currentThread]);
});
NSLog(@"4------>>>%@",[NSThread currentThread]);
}
結(jié)果:
2018-04-01 10:31:14.552817+0800 GCD[56662:12481282] 4------>>><NSThread: 0x604000069640>{number = 1, name = main}
2018-04-01 10:31:14.552818+0800 GCD[56662:12481369] 2------>>><NSThread: 0x6000002742c0>{number = 4, name = (null)}
2018-04-01 10:31:14.552820+0800 GCD[56662:12481372] 3------>>><NSThread: 0x600000274240>{number = 5, name = (null)}
2018-04-01 10:31:14.552838+0800 GCD[56662:12481370] 1------>>><NSThread: 0x604000268a40>{number = 3, name = (null)}
分析:
這是最常用的一種方式,因?yàn)樗钱惒疥?duì)列,所以它是不會(huì)堵塞當(dāng)前線程的,4會(huì)先執(zhí)行,由于是并發(fā)隊(duì)列,前面的任務(wù)開(kāi)始執(zhí)行了,后面的任務(wù)便可以出隊(duì)列,異步執(zhí)行導(dǎo)致為它單獨(dú)開(kāi)辟線程執(zhí)行,所以1,2,3會(huì)在不同的線程中執(zhí)行,如果是1,2,3分別是耗時(shí)的操作,那么它們的執(zhí)行順序?qū)⒉坏枚?隨機(jī);
五. 主隊(duì)列 + 同步執(zhí)行
-
當(dāng)前線程為主線程:
- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"1------>>>%@",[NSThread currentThread]); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"2------>>>%@",[NSThread currentThread]); }); NSLog(@"3------>>>%@",[NSThread currentThread]);
結(jié)果:打印了1---->>>main后崩潰;
分析:
很多博客中把這個(gè)案例都說(shuō)的很好,這里我講下我的理解,首先viewDidLoad本身就是主線程中執(zhí)行的,被放在了主隊(duì)列中,當(dāng)執(zhí)行它的時(shí)候發(fā)現(xiàn),里面有個(gè)同步,必須等block里面的任務(wù)執(zhí)行完才行,也就是必須執(zhí)行2,但是這時(shí)候,2又被放入了主隊(duì)列,主隊(duì)列是串行的,很顯然,在主隊(duì)列中2是排在viewDidload任務(wù)后面的,那么它必須等viewDidload執(zhí)行完才可以去執(zhí)行,然后viewDidload必須等2執(zhí)行完,才算完,,,,這樣陷入死循環(huán),就崩潰了;
- 當(dāng)前線程為分線程:
- (void) GCDTest5
{
NSLog(@"1------>>>%@",[NSThread currentThread]);
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2------>>>%@",[NSThread currentThread]);
});
NSLog(@"3------>>>%@",[NSThread currentThread]);
}
結(jié)果:
2018-04-01 10:41:49.864270+0800 GCD[57307:12495333] 1------>>><NSThread: 0x604000664d80>{number = 3, name = (null)}
2018-04-01 10:41:49.868650+0800 GCD[57307:12495215] 2------>>><NSThread: 0x604000261d00>{number = 1, name = main}
2018-04-01 10:41:49.869738+0800 GCD[57307:12495333] 3------>>><NSThread: 0x604000664d80>{number = 3, name = (null)}
分析:
這里不同于上面的點(diǎn)在于,GCDTest5被放在了分線程,那么就好辦了,分線程中執(zhí)行GCDTest5任務(wù)時(shí)遇到同步,就會(huì)一直等,2被放到了主隊(duì)列,那么2會(huì)在所有它前面的主隊(duì)列中的任務(wù)執(zhí)行完后執(zhí)行,執(zhí)行完后分線程中GCDTest5會(huì)繼續(xù)往后執(zhí)行;
六. 主隊(duì)列 + 異步執(zhí)行
- (void) GCDTest6
{
NSLog(@"1------>>>%@",[NSThread currentThread]);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"2------>>>%@",[NSThread currentThread]);
});
NSLog(@"3------>>>%@",[NSThread currentThread]);
}
結(jié)果:
2018-04-01 10:46:06.757861+0800 GCD[57573:12500988] 1------>>><NSThread: 0x60000007f500>{number = 1, name = main}
2018-04-01 10:46:06.758154+0800 GCD[57573:12500988] 3------>>><NSThread: 0x60000007f500>{number = 1, name = main}
2018-04-01 10:46:06.764103+0800 GCD[57573:12500988] 2------>>><NSThread: 0x60000007f500>{number = 1, name = main}
分析:
這個(gè)沒(méi)什么懸念,主隊(duì)列中的任務(wù)是在主線程中執(zhí)行的,而且主隊(duì)列是串行隊(duì)列;
6舷嗡、線程中的通信方式
7轴猎、使用GCD需要注意的地方
8、GCD中的其他重要函數(shù)
-
隊(duì)列組
當(dāng)有多個(gè)異步任務(wù)的時(shí)候,但是我們需要這些任務(wù)都完成的時(shí)候,才執(zhí)行一些操作,由于是異步,我們無(wú)法保證所有任務(wù)的結(jié)果,這時(shí)候就可以用到dispatch_group, 比如說(shuō)我們一個(gè)首頁(yè)界面有好多請(qǐng)求,但是我想等所有請(qǐng)求都有結(jié)果的時(shí)候再顯示,不然顯示不全會(huì)很難看,這時(shí)候我們可以這樣:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 3; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_group_notify(group, queue, ^{
NSLog(@"---所有任務(wù)完成了--->>");
});