iOS中目前有4套多線程方案,分別是
Pthreads
NSThread
GCD
NSOperation & NSOperationQueue
提到多線程,毫無(wú)疑問(wèn)都能回答出這四種!
今天我們主要回顧一下我們最熟悉!最常用的“GCD”;
二.GCD
GCD 完整字段為Grand Centeral Dispatch,是蘋果為多核的并行運(yùn)算提出的解決方案,所以會(huì)自動(dòng)合理地利用更多的CPU內(nèi)核(比如雙核、四核),最重要的是它會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程),完全不需要我們管理,我們只需要告訴該干什么就行。GCD使用的是C語(yǔ)言,不過(guò)由于使用了Block,使用起來(lái)更加方便靈活,目前基本大家都使用GCD解決多線程問(wèn)題。
GCD的優(yōu)勢(shì)
GCD是蘋果為多核的并行運(yùn)算提出的解決方案
GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核
GCD會(huì)自動(dòng)管理線程的生命周期創(chuàng)建線程調(diào)度任務(wù)``銷毀線程
程序員只需要告訴GCD想要執(zhí)行什么任務(wù),不需要管理任何線程管理代碼
三.任務(wù)和隊(duì)列
在GCD中,加入了兩個(gè)非常重要的概念:任務(wù)和隊(duì)列
任務(wù):即你想要進(jìn)行的操作,比如說(shuō)網(wǎng)絡(luò)請(qǐng)求,數(shù)據(jù)緩存等,在GCD中就是一個(gè)Block,所以添加任務(wù)十分方便。任務(wù)有兩種執(zhí)行方式:同步執(zhí)行和異步執(zhí)行,他們之間的區(qū)別是 是否會(huì)創(chuàng)建新的線程
同步(sync)操作:會(huì)阻塞當(dāng)前線程并等待Block中的任務(wù)執(zhí)行完畢,然后當(dāng)前線程才會(huì)繼續(xù)往下運(yùn)行
異步(async)操作:當(dāng)前線程會(huì)直接往下執(zhí)行,不會(huì)阻塞當(dāng)前線程
同步(sync)和異步(async)的主要區(qū)別在于會(huì)不會(huì)阻塞當(dāng)前線程,直到Block中的任務(wù)執(zhí)行完畢
隊(duì)列:用于存放任務(wù),一共有兩種隊(duì)列
串行隊(duì)列中的任務(wù)會(huì)根據(jù)隊(duì)列的定義FIFO的執(zhí)行,一個(gè)接一個(gè)的,先進(jìn)先出的執(zhí)行
放到串行隊(duì)列的任務(wù),GCD會(huì)FIFO(先進(jìn)先出)地取出來(lái)一個(gè),執(zhí)行一個(gè),然后取下一個(gè),這樣一個(gè)一個(gè)的執(zhí)行。
放到并行隊(duì)列的任務(wù),GCD也會(huì)FIFO的取出來(lái),但不同的是,它取出來(lái)一個(gè)就會(huì)放到別的線程,然后再取出來(lái)一個(gè)又放到另一個(gè)的線程,這樣由于取的動(dòng)作很快,忽略不計(jì),看起來(lái),所以的任務(wù)都是一起執(zhí)行的,不過(guò)需要注意,GCD會(huì)根據(jù)系統(tǒng)資源控制并行的數(shù)量,所以如果任務(wù)很多,它并不會(huì)讓所有任務(wù)同時(shí)執(zhí)行。
同步執(zhí)行異步執(zhí)行
串行隊(duì)列當(dāng)前線程,一個(gè)一個(gè)執(zhí)行其他線程,一個(gè)一個(gè)執(zhí)行
并行隊(duì)列當(dāng)前線程,一個(gè)一個(gè)執(zhí)行開很多線程,一起執(zhí)行
四.創(chuàng)建隊(duì)列
主隊(duì)列:這是一個(gè)特殊的串行隊(duì)列,用于刷新UI,任何需要刷新UI的工作都要在主隊(duì)列執(zhí)行,所以一般耗時(shí)的任務(wù)都要放到別的線程執(zhí)行
//Objective-C
dispatch_queue_t queue = dispatch_get_main_queue();
//Swift
let queue = DispatchQueue.main
自己創(chuàng)建的隊(duì)列:第一個(gè)參數(shù)是標(biāo)識(shí)符,用于Debug的時(shí)候標(biāo)識(shí)唯一的隊(duì)列,可以為空。具體可以查看Xcode的文檔查看參數(shù)意義
自己可以創(chuàng)建串行隊(duì)列,也可以創(chuàng)建并行隊(duì)列,它有兩個(gè)參數(shù),第一個(gè)上面已經(jīng)說(shuō)了,第二個(gè)參數(shù)用了表示創(chuàng)建的隊(duì)列是串行的還是并行的,傳入DISPATCH_QUEUE_SERIAL或NULL標(biāo)示創(chuàng)建串行隊(duì)列,傳入DISPATCH_QUEUE_CONCURRENT表示創(chuàng)建并行隊(duì)列
//Objective-C
//串行隊(duì)列
dispatch_queue_t serialQueue = dispatch_queue_create("serial1", NULL);
dispatch_queue_t seqialQueue = dispatch_queue_create("serial2", DISPATCH_QUEUE_SERIAL);
//并行隊(duì)列
dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
//Swift
//主隊(duì)列(串行)
let queue = DispatchQueue.main
全局并行隊(duì)列:只要是并行任務(wù)一般都加入到這個(gè)隊(duì)列。這是系統(tǒng)提供的一個(gè)并發(fā)隊(duì)列
//全局并發(fā)隊(duì)列
//Objective-C
dispatch_queue_t globeQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//Swift
let serialQueue = DispatchQueue.global()
五.創(chuàng)建任務(wù)
同步任務(wù):會(huì)阻塞當(dāng)前線程(SYNC)
Objective-C
//同步任務(wù)
dispatch_sync(serialQueue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
異步任務(wù):不會(huì)阻塞當(dāng)前線程(ASYNC)
Objective-C
//異步任務(wù)
dispatch_sync(serialQueue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
示例一:
以下代碼在主線程調(diào)用,結(jié)果是什么?
NSLog(@"before - %@",[NSThread currentThread]);
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"sync- %@",[NSThread currentThread]);
});
NSLog(@"after - %@",[NSThread currentThread]);
答案:只會(huì)打印第一句:before - {number = 1, name = main},然后主線程就卡死,程序奔潰
解釋:同步任務(wù)會(huì)阻塞當(dāng)前線程,然后把Block中的任務(wù)放到指定的隊(duì)列中執(zhí)行,只有等到Block中的任務(wù)完成后才會(huì)讓線程繼續(xù)往下運(yùn)行。
那么這里的步驟就是:打印完第一句后,dispatch_sync立即阻塞當(dāng)前的主線程,然后把Block中的任務(wù)放到main_queue,可是main_queue中的任務(wù)會(huì)被取出來(lái)放到主線程中執(zhí)行,但主線程這個(gè)時(shí)候已經(jīng)被阻塞了,所以Block中的任務(wù)就不能完成,它不完成,dispatch_sync就會(huì)一直阻塞主線程,這就是死鎖現(xiàn)象,導(dǎo)致主線程一直卡死
示例二
以下代碼會(huì)產(chǎn)生什么結(jié)果?
dispatch_queue_t serialQueue = dispatch_queue_create("serial1", NULL);
NSLog(@"begain - %@",[NSThread currentThread]);
dispatch_async(serialQueue, ^{
NSLog(@"beforeSync: %@",[NSThread currentThread]);
dispatch_sync(serialQueue, ^{
NSLog(@"sync-: %@",[NSThread currentThread]);
});
NSLog(@" afterSync-: %@",[NSThread currentThread]);
});
NSLog(@"last-: %@",[NSThread currentThread]);
}
答案:
2017-03-20 16:59:11.436 TestGcd[8245:264276] begain - {number = 1, name = main}
2017-03-20 16:59:11.438 TestGcd[8245:264276] last: {number = 1, name = main}
2017-03-20 16:59:11.438 TestGcd[8245:264329] beforeSync: {number = 3, name = (null)}
(lldb)
很明顯sync-: %@和afterSync-: %@沒(méi)有打印出來(lái),這是為什么?我們來(lái)一步步分析一下:
分析:
使用DISPATCH_QUEUE_SERIAL這個(gè)參數(shù),創(chuàng)建一個(gè)串行隊(duì)列
打印begain - %@這句
dispatch_async異步執(zhí)行,所以當(dāng)前線程不會(huì)阻塞,于是有了2條線程,一條當(dāng)前線程繼續(xù)往下打印出last-: %@這句,另一條執(zhí)行Block中的內(nèi)容打印beforeSync: %@這句,因?yàn)檫@兩條線程是并行的,所以打印的先后順序無(wú)所謂
注意,高潮來(lái)了…現(xiàn)在的情況和上個(gè)例子一樣,dispatch_sync同步執(zhí)行,于是它所在的線程會(huì)被阻塞,一直等到sync里的任務(wù)執(zhí)行完才會(huì)繼續(xù)往下。于是sync就高興的把自己Block中的任務(wù)放到serialQueue中,可誰(shuí)想serialQueue是一個(gè)串行隊(duì)列,一次執(zhí)行一個(gè)任務(wù),所以sync的Block必須等到前一個(gè)任務(wù)執(zhí)行完畢,可萬(wàn)萬(wàn)沒(méi)想到的是serialQueue正在執(zhí)行的任務(wù)就是被sync阻塞了的那個(gè),于是又發(fā)生了死鎖,所以sync所在的線程被卡死了,剩下的兩句代碼自然不會(huì)打印。
六.隊(duì)列組
隊(duì)列組可以將很多隊(duì)列添加到一個(gè)組里,這樣做的好處是,當(dāng)這個(gè)組里所有的任務(wù)都執(zhí)行完了,隊(duì)列組會(huì)通過(guò)一個(gè)方法通知我們。下面是使用方法,這是一個(gè)很實(shí)用的功能
//1.創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
//2.創(chuàng)建隊(duì)列
dispatch_queue_t queueGroup = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//3.多次使用隊(duì)列組的方法執(zhí)行任務(wù),只有異步方法
//3.1。執(zhí)行3次循環(huán)
dispatch_group_async(group, queueGroup, ^{
for (NSUInteger i = 0; i < 3; i++) {
NSLog(@"group - 01 - %@",[NSThread currentThread]);
}
});
//3.2。主隊(duì)列執(zhí)行8次循環(huán)
dispatch_group_async(group, dispatch_get_main_queue(), ^{
for (NSInteger i = 0; i < 8; i++) {
NSLog(@"group - 02 - %@",[NSThread currentThread]);
}
});
//3.3.執(zhí)行5次循環(huán)
dispatch_group_async(group, queueGroup, ^{
for (NSUInteger i = 0; i < 5; i++) {
NSLog(@"group - 03 - %@", [ NSThread currentThread]);
}
});
//4.都完成后會(huì)自動(dòng)通知
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"完成 - %@",[NSThread currentThread]);
});
打印結(jié)果
2017-03-20 17:43:17.266 TestGcd[9020:294003] group - 01 - {number = 3, name = (null)}
2017-03-20 17:43:17.266 TestGcd[9020:294005] group - 03 - {number = 4, name = (null)}
2017-03-20 17:43:17.267 TestGcd[9020:294003] group - 01 - {number = 3, name = (null)}
2017-03-20 17:43:17.267 TestGcd[9020:294005] group - 03 - {number = 4, name = (null)}
2017-03-20 17:43:17.268 TestGcd[9020:294003] group - 01 - {number = 3, name = (null)}
2017-03-20 17:43:17.269 TestGcd[9020:294005] group - 03 - {number = 4, name = (null)}
2017-03-20 17:43:17.272 TestGcd[9020:294005] group - 03 - {number = 4, name = (null)}
2017-03-20 17:43:17.272 TestGcd[9020:294005] group - 03 - {number = 4, name = (null)}
2017-03-20 17:43:17.280 TestGcd[9020:293944] group - 02 - {number = 1, name = main}
2017-03-20 17:43:17.281 TestGcd[9020:293944] group - 02 - {number = 1, name = main}
2017-03-20 17:43:17.284 TestGcd[9020:293944] group - 02 - {number = 1, name = main}
2017-03-20 17:43:17.285 TestGcd[9020:293944] group - 02 - {number = 1, name = main}
2017-03-20 17:43:17.286 TestGcd[9020:293944] group - 02 - {number = 1, name = main}
2017-03-20 17:43:17.294 TestGcd[9020:293944] group - 02 - {number = 1, name = main}
2017-03-20 17:43:17.312 TestGcd[9020:293944] group - 02 - {number = 1, name = main}
2017-03-20 17:43:17.312 TestGcd[9020:293944] group - 02 - {number = 1, name = main}
2017-03-20 17:43:17.315 TestGcd[9020:293944] 完成 - {number = 1, name = main}
以上就是GCD的基本功能,但它的能力遠(yuǎn)不止這寫些,之后我會(huì)更新它的其它用途? 有寶貴意見歡迎糾正!??!