(一)、線程的概念和與生命周期
進(jìn)程:可以簡(jiǎn)單理解為進(jìn)程為一個(gè)應(yīng)用程序
線程:是CPU調(diào)度和分派的基本單位
下圖是線程狀態(tài)示意圖,從圖中可以看出線程的生命周期是:新建 - 就緒 - 運(yùn)行 - 阻塞 - 死亡

(二)、多線程的四種解決方案
多線程的四種解決方案分別是:OC主要使用NSThread,GCD, NSOperation,pthread為跨平臺(tái)的。

(三)、GCD的特點(diǎn)和運(yùn)用場(chǎng)景
GCD是蘋(píng)果提供的,底層為C 語(yǔ)言寫(xiě)的,一套多線程API ,它具有操作簡(jiǎn)單、執(zhí)行高效特點(diǎn)。GCD以block為基本單位,一個(gè)block中的代碼可以視為一個(gè)任務(wù)。GCD中有兩大最重要的概念,分別是“隊(duì)列”和“執(zhí)行方式”。開(kāi)發(fā)者要做的只是定義執(zhí)行的任務(wù)并追加到適當(dāng)?shù)?Dispatch Queue 中。
(1)GCD術(shù)語(yǔ)解釋

(2)GCD幾種隊(duì)列獲取方式
- 主線程隊(duì)列(The main queue):通過(guò)
dispatch_get_main_queue()來(lái)獲得,這是一個(gè)串行隊(duì)列。提交至main queue的任務(wù)會(huì)在主線程中執(zhí)行,app中的main函數(shù)默認(rèn)在主線程執(zhí)行,和UI相關(guān)的修改必須使用Main Queue。
- 全局并發(fā)隊(duì)列(Global queue): 通過(guò)調(diào)用
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)函數(shù)來(lái)獲?。梢栽O(shè)置優(yōu)先級(jí)),全局并發(fā)隊(duì)列由整個(gè)進(jìn)程共享,有高、中(默認(rèn))、低、后臺(tái)四個(gè)優(yōu)先級(jí)別。
自定義隊(duì)列(Custom queue):通過(guò)調(diào)用
dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT)來(lái)創(chuàng)建,DISPATCH_QUEUE_CONCURRENT并行隊(duì)列,可以并發(fā)的執(zhí)行多個(gè)任務(wù),但是執(zhí)行完成的順序是隨機(jī)的。DISPATCH_QUEUE_SERIAL串行隊(duì)列,隊(duì)列里的任務(wù)都是串行執(zhí)行的,一個(gè)執(zhí)行完再執(zhí)行下一個(gè)。Group queue (隊(duì)列組):通過(guò)調(diào)用
dispatch_group_create()來(lái)獲取將多線程進(jìn)行分組,最大的好處是可獲知所有線程的完成情況。 通過(guò)dispatch_group_notify可以直接監(jiān)聽(tīng)組里所有線程完成情況。
(3)GCD隊(duì)列異步dispatch_async、同步dispatch_sync執(zhí)行方式區(qū)別

(3.1)GCD常用場(chǎng)景一:實(shí)現(xiàn)多個(gè)異步線程同步操作
我們也知道異步函數(shù) + 串行隊(duì)列實(shí)現(xiàn)任務(wù)同步執(zhí)行更加簡(jiǎn)單。
不過(guò)異步函數(shù) + 串行隊(duì)列的弊端也是非常明顯的:
因?yàn)槭钱惒胶瘮?shù),所以系統(tǒng)會(huì)開(kāi)啟新(子)線程,又因?yàn)槭谴嘘?duì)列,所以系統(tǒng)只會(huì)開(kāi)啟一個(gè)子線程。
這就導(dǎo)致了所有的任務(wù)都是在這個(gè)子線程中同步的一個(gè)一個(gè)執(zhí)行。喪失了并發(fā)執(zhí)行的可能性。
雖然可以完成任務(wù),但是卻沒(méi)有充分發(fā)揮CPU多核(多線程)的優(yōu)勢(shì)。
/*
* 同步和異步?jīng)Q定了是否開(kāi)啟新線程(或者說(shuō)是否具有開(kāi)啟新線程的能力),
* 串行和并發(fā)決定了任務(wù)的執(zhí)行方式——串行執(zhí)行還是并發(fā)執(zhí)行(或者說(shuō)開(kāi)啟多少條新線程)
*/
- (void)dispatchAsyncForSerial {
dispatch_queue_t serialQueue = dispatch_queue_create("dispatchAsyncForSerial", DISPATCH_QUEUE_SERIAL);
NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:12];
for (int i = 0; i < 12; i++) {
dispatch_async(serialQueue, ^{
[arrayM addObject:[NSNumber numberWithInt:i]];
NSLog(@"currentThread = %@, %@",[NSThread currentThread],[NSNumber numberWithInt:i]);
});
}
}
輸出:nunber = 5 說(shuō)明開(kāi)啟了一個(gè)新線程在執(zhí)行
currentThread = <NSThread: 0x6000036b2100>{number = 5, name = (null)}, 0
currentThread = <NSThread: 0x6000036b2100>{number = 5, name = (null)}, 1
currentThread = <NSThread: 0x6000036b2100>{number = 5, name = (null)}, 2
currentThread = <NSThread: 0x6000036b2100>{number = 5, name = (null)}, 3
currentThread = <NSThread: 0x6000036b2100>{number = 5, name = (null)}, 4
currentThread = <NSThread: 0x6000036b2100>{number = 5, name = (null)}, 5
currentThread = <NSThread: 0x6000036b2100>{number = 5, name = (null)}, 6
currentThread = <NSThread: 0x6000036b2100>{number = 5, name = (null)}, 7
currentThread = <NSThread: 0x6000036b2100>{number = 5, name = (null)}, 8
currentThread = <NSThread: 0x6000036b2100>{number = 5, name = (null)}, 9
currentThread = <NSThread: 0x6000036b2100>{number = 5, name = (null)}, 10
currentThread = <NSThread: 0x6000036b2100>{number = 5, name = (null)}, 11
- Dispatch Semaphore 信號(hào)量:
1: 用GCD的信號(hào)量來(lái)實(shí)現(xiàn)異步線程同步操作
2: 保證線程安全,為線程加鎖
/*
*1.dispatch_semaphore_create:創(chuàng)建一個(gè)Semaphore并初始化信號(hào)的總量
* 2.dispatch_semaphore_signal:發(fā)送一個(gè)信號(hào),讓信號(hào)總量加1
* 3.dispatch_semaphore_wait:可以使總信號(hào)量減1,當(dāng)信號(hào)總量為0時(shí)就會(huì)一直等待(阻塞所在線程),否則就可以正常執(zhí)行。
*/
//MARK: - 用GCD的信號(hào)量來(lái)實(shí)現(xiàn)異步線程同步操作 1:同步且并發(fā)執(zhí)行發(fā)揮多核優(yōu)勢(shì) 2:保證線程安全,為線程加鎖
- (void)dispatchSemaphore {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:12];
// 創(chuàng)建為1的信號(hào)量
dispatch_semaphore_t sem = dispatch_semaphore_create(1);
for (int i = 0; i < 12; i++) {
dispatch_async(queue, ^{
// 等待信號(hào)量
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
[arrayM addObject:[NSNumber numberWithInt:i]];
NSLog(@"currentThread = %@, %@",[NSThread currentThread],[NSNumber numberWithInt:i]);
// 發(fā)送信號(hào)量
dispatch_semaphore_signal(sem);
});
}
}
輸出:在多個(gè)線程下并發(fā)執(zhí)行完任務(wù)
currentThread = <NSThread: 0x600002db7280>{number = 5, name = (null)}, 0
currentThread = <NSThread: 0x600002df9480>{number = 6, name = (null)}, 2
currentThread = <NSThread: 0x600002dfa940>{number = 7, name = (null)}, 1
currentThread = <NSThread: 0x600002da3180>{number = 8, name = (null)}, 3
currentThread = <NSThread: 0x600002dc95c0>{number = 4, name = (null)}, 4
currentThread = <NSThread: 0x600002db8280>{number = 3, name = (null)}, 5
currentThread = <NSThread: 0x600002df9500>{number = 9, name = (null)}, 6
currentThread = <NSThread: 0x600002dfa7c0>{number = 10, name = (null)}, 7
currentThread = <NSThread: 0x600002db7280>{number = 5, name = (null)}, 9
currentThread = <NSThread: 0x600002da37c0>{number = 11, name = (null)}, 8
currentThread = <NSThread: 0x600002da4f40>{number = 12, name = (null)}, 10
currentThread = <NSThread: 0x600002da5080>{number = 13, name = (null)}, 11
(3.2)GCD常用場(chǎng)景二:dispatch_barrier_sync實(shí)現(xiàn)多讀單寫(xiě)

/* 實(shí)現(xiàn)多讀單寫(xiě)
* 這里的dispatch_barrier_sync上的隊(duì)列要和需要阻塞的任務(wù)在同一隊(duì)列上,否則是無(wú)效的。
* 柵欄函數(shù)任務(wù):在他之前所有的任務(wù)執(zhí)行完畢,并且在它后面的任務(wù)開(kāi)始之前,期間不會(huì)有其他的任務(wù)執(zhí)行,這樣比較好的促使 寫(xiě)操作一個(gè)接一個(gè)寫(xiě) (寫(xiě)寫(xiě)互斥),不會(huì)亂!
* 運(yùn)用對(duì)NSMutableArray 多讀單寫(xiě)保證線程安全
*/
//MARK: - 實(shí)現(xiàn)多讀單寫(xiě)
-(void)dispatchBarrier {
[self setObject:@"hello kitty" forKey:@"message" waitTime:4];
for (NSInteger i = 0; i < 10; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self valueForKey:@"message"];
});
}
NSLog(@"寫(xiě)操作hello kitty后的任務(wù)");
[self setObject:@"hello jack" forKey:@"message" waitTime:2];
for (NSInteger i = 0; i < 10; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self valueForKey:@"message"];
});
}
NSLog(@"寫(xiě)操作hello jack后的任務(wù)");
[self setObject:@"hello weilian" forKey:@"message" waitTime:3];
for (NSInteger i = 0; i < 10; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self valueForKey:@"message"];
});
}
NSLog(@"寫(xiě)操作hello weilian后的任務(wù)");
}
- (id)valueForKey:(NSString *)key {
id __block obj;
// 在調(diào)用此函數(shù)的線程同步執(zhí)行所有添加到 queue 隊(duì)列的讀任務(wù),
// 如果前邊有寫(xiě)的任務(wù), 由于 barrier 堵塞隊(duì)列, 只能等待寫(xiě)任務(wù)完成
dispatch_sync(_dictQueue, ^{
obj = [self.dict valueForKey:key];
NSLog(@"read thread = %@, %@",[NSThread currentThread],obj);
});
return obj;
}
- (void)setObject:(id)obj forKey:(id<NSCopying>)key waitTime:(int)sleepNum {
// 重點(diǎn):dispatch_barrier_async(),異步柵欄調(diào)用;柵欄函數(shù)堵塞的是隊(duì)列,添加到隊(duì)列中的寫(xiě)操作任務(wù), 只能依次按照添加順序進(jìn)行修改,不會(huì)出現(xiàn)資源搶奪現(xiàn)象
// 在子線程完成寫(xiě)任務(wù),因?yàn)轫樞驁?zhí)行只需要開(kāi)辟一個(gè)線程
// 等待前面任務(wù)執(zhí)行完成后開(kāi)始寫(xiě)
// 寫(xiě)的完成任務(wù)后, 才能繼續(xù)執(zhí)行后邊添加進(jìn)此隊(duì)列的任務(wù)
dispatch_barrier_async(_dictQueue, ^{
sleep(sleepNum); //模擬耗時(shí)操作
[self.dict setObject:obj forKey:key];
NSLog(@"write thread = %@, %@",[NSThread currentThread],obj);
});
}
輸出:
- 先打印初nslog 的日志,dispatch_barrier_async使用并發(fā)且不堵塞當(dāng)前線程(當(dāng)前主線程),
- 我們可以看到 三個(gè)寫(xiě)操作雖然他們耗時(shí)不同,但都按照入隊(duì)的順序依次執(zhí)行,hello kitty-> hello jack-> hello weilian
- 后面打印20來(lái)個(gè) hello weilian 是因?yàn)?在讀的過(guò)程中有插入寫(xiě)的任務(wù),只有等 dispatch_barrier_async()執(zhí)行完才能執(zhí)行后邊添加進(jìn)此隊(duì)列讀任務(wù)
寫(xiě)操作hello kitty后的任務(wù)
寫(xiě)操作hello jack后的任務(wù)
寫(xiě)操作hello weilian后的任務(wù)
write thread = <NSThread: 0x600000ea2600>{number = 5, name = (null)}, hello kitty
read thread = <NSThread: 0x600000ec6580>{number = 2, name = (null)}, hello kitty
read thread = <NSThread: 0x600000ec74c0>{number = 7, name = (null)}, hello kitty
read thread = <NSThread: 0x600000ec1000>{number = 3, name = (null)}, hello kitty
read thread = <NSThread: 0x600000ee4ac0>{number = 9, name = (null)}, hello kitty
read thread = <NSThread: 0x600000edc5c0>{number = 10, name = (null)}, hello kitty
write thread = <NSThread: 0x600000edc5c0>{number = 10, name = (null)}, hello jack
read thread = <NSThread: 0x600000ed7980>{number = 11, name = (null)}, hello jack
read thread = <NSThread: 0x600000ee0040>{number = 12, name = (null)}, hello jack
read thread = <NSThread: 0x600000ed5300>{number = 13, name = (null)}, hello jack
write thread = <NSThread: 0x600000ee0040>{number = 12, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ee0140>{number = 14, name = (null)}, hello weilian
read thread = <NSThread: 0x600000edd340>{number = 17, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ed2540>{number = 16, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ee02c0>{number = 15, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ee00c0>{number = 18, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ed25c0>{number = 20, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ede000>{number = 21, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ee4680>{number = 19, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ee4980>{number = 23, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ed2640>{number = 24, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ee0240>{number = 22, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ed2680>{number = 27, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ede100>{number = 25, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ee0300>{number = 26, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ed2700>{number = 29, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ee46c0>{number = 28, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ee4740>{number = 31, name = (null)}, hello weilian
read thread = <NSThread: 0x600000edc600>{number = 30, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ee47c0>{number = 32, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ede1c0>{number = 33, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ede140>{number = 35, name = (null)}, hello weilian
read thread = <NSThread: 0x600000ee0340>{number = 34, name = (null)}, hello weilian
(3.3)GCD常用場(chǎng)景三:dispatch_group實(shí)現(xiàn)多個(gè)網(wǎng)絡(luò)請(qǐng)求的同步問(wèn)題
/**dispatch_group 可以實(shí)現(xiàn)監(jiān)聽(tīng)一組任務(wù)是否完成,完成后得到通知執(zhí)行其他的操作
* dispatch_group_enter標(biāo)志著一個(gè)任務(wù)追加到 group,執(zhí)行一次,相當(dāng)于 group 中未執(zhí)行完畢任務(wù)數(shù)+1
* dispatch_group_leave標(biāo)志著一個(gè)任務(wù)離開(kāi)了 group,執(zhí)行一次,相當(dāng)于 group 中未執(zhí)行完畢任務(wù)數(shù)-1。
* 用dispatch_async(queue, ^{})則必須配合enter和level才能最后執(zhí)行notify;用dispatch_group_async來(lái)提交任務(wù)如果是異步的,也必須配合enter和level,
* 當(dāng) group 中未執(zhí)行完畢任務(wù)數(shù)為0的時(shí)候,才會(huì)使dispatch_group_wait解除阻塞,以及執(zhí)行追加到dispatch_group_notify中的任務(wù)。
*/
- (void)dispatch_group_test {
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//A1耗時(shí)操作
dispatch_group_enter(group);
dispatch_async(queue, ^{
sleep(1.5);
NSLog(@"A1耗時(shí)操作---%@",[NSThread currentThread]);
dispatch_group_leave(group);
});
//A2耗時(shí)操作
dispatch_group_async(group, queue, ^{
sleep(3);
NSLog(@"A2耗時(shí)操作---%@",[NSThread currentThread]);
});
//B網(wǎng)絡(luò)請(qǐng)求
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
sleep(4);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"B網(wǎng)絡(luò)請(qǐng)求---%@",[NSThread currentThread]);
dispatch_group_leave(group);
});
});
//C網(wǎng)絡(luò)請(qǐng)求
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
sleep(2);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"C網(wǎng)絡(luò)請(qǐng)求---%@",[NSThread currentThread]);
dispatch_group_leave(group);
});
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//等前面的異步操作都執(zhí)行完畢后,回到主線程.
//模擬耗時(shí)操作
sleep(1);
//打印當(dāng)前線程
NSLog(@"group---end---%@",[NSThread currentThread]);
});
NSLog(@"code end");
}
輸出:
group---begin
code end
A1耗時(shí)操作---<NSThread: 0x60000293dc40>{number = 5, name = (null)}
C網(wǎng)絡(luò)請(qǐng)求---<NSThread: 0x600002934740>{number = 1, name = main}
A2耗時(shí)操作---<NSThread: 0x60000297e9c0>{number = 6, name = (null)}
B網(wǎng)絡(luò)請(qǐng)求---<NSThread: 0x600002934740>{number = 1, name = main}
group---end---<NSThread: 0x600002934740>{number = 1, name = main}
總結(jié):
- dispatch_group是一個(gè)初始值為L(zhǎng)ONG_MAX的信號(hào)量,group中的任務(wù)完成是判斷其value是否恢復(fù)成初始值。
- dispatch_group_enter和dispatch_group_leave必須成對(duì)使用并且支持嵌套。
- 如果dispatch_group_enter比dispatch_group_leave多,由于value不等于dsema_orig不會(huì)走到喚醒邏輯,dispatch_group_notify中的任務(wù)無(wú)法執(zhí)行或者dispatch_group_wait收不到信號(hào)而卡住線程。
- 如果是dispatch_group_leave多,則會(huì)引起崩潰。
(3.4)GCD造成主線死鎖的情況
/*
* 1、主隊(duì)列同步任務(wù),造成相互等待,死鎖
* 2、串行隊(duì)列(同步/異步任務(wù))嵌套同步任務(wù),造成相互等待,死鎖
*/
- (void)deathLock {
//1、主隊(duì)列同步任務(wù),造成相互等待,死鎖
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"deallock");
});
//2、串行隊(duì)列(同步/異步任務(wù))嵌套同步任務(wù),造成相互等待,死鎖
dispatch_queue_t serialQueue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
dispatch_sync(serialQueue, ^{
NSLog(@"deadlock");
});
});
}