iOS GCD開(kāi)發(fā)運(yùn)用場(chǎng)景

(一)、線程的概念和與生命周期

進(jìn)程:可以簡(jiǎn)單理解為進(jìn)程為一個(gè)應(yīng)用程序

線程:是CPU調(diào)度和分派的基本單位

下圖是線程狀態(tài)示意圖,從圖中可以看出線程的生命周期是:新建 - 就緒 - 運(yùn)行 - 阻塞 - 死亡


線程的狀態(tài)與生命周期.png

(二)、多線程的四種解決方案

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


多線程的四種解決方案.png

(三)、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ǔ)解釋

術(shù)語(yǔ)解釋.png

(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ū)別

CGCD隊(duì)列同步異步執(zhí)行區(qū)別.png

(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ě)

dispatch_barrier_sync.jpeg
/* 實(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");
        });
    });
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容