iOS線程、隊(duì)列解析

基礎(chǔ)概念理解

  • 進(jìn)程:進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序。每個(gè)進(jìn)程之間是獨(dú)立的,每個(gè)進(jìn)程運(yùn)行在其專用的且受保護(hù)的內(nèi)存中。比如:Windows系統(tǒng)中常用的殺進(jìn)程,就是徹底關(guān)閉一個(gè)程序;手機(jī)端的愛口袋、我要聘等APP可以看成一個(gè)進(jìn)程。

  • 線程:線程是進(jìn)程的基本單位,一個(gè)進(jìn)程的所有任務(wù)都在線程中執(zhí)行,進(jìn)程要想執(zhí)行任務(wù),必須得有一個(gè)或多個(gè)線程。程序啟動(dòng)時(shí),默認(rèn)會(huì)開啟一條線程,這條線程稱之為主線程或者UI線程。

  • 隊(duì)列:隊(duì)列是任務(wù)的集合,強(qiáng)調(diào)的是一種靜態(tài)表示?;蛘呖梢哉f,隊(duì)列是按先進(jìn)先出(FIFO)管理任務(wù)對象的數(shù)據(jù)結(jié)構(gòu);通常我們將隊(duì)列分為4種:串行隊(duì)列、并發(fā)隊(duì)列、主隊(duì)列、全局隊(duì)列。

  • 同步(sync):任務(wù)一個(gè)接著一個(gè),前一個(gè)沒有執(zhí)行完,后面不能執(zhí)行,沒有創(chuàng)建新線程的能力。

  • 異步(async):開啟多個(gè)新線程,任務(wù)同一時(shí)間可以一起執(zhí)行。異步有開啟新線程的能力,但是不一定會(huì)開啟新線程(在主線程上就不會(huì)開新線程)。嚴(yán)格來說,異步才有多線程的概念,是多線程的代名詞。

多線程的意義

  • 優(yōu)點(diǎn):
    1、能適當(dāng)提高程序的執(zhí)行效率
    2、能適當(dāng)提高資源利用率(CPU、內(nèi)存)
    3、線程上的任務(wù)執(zhí)行完成后,線程會(huì)自動(dòng)銷毀
  • 缺點(diǎn):
    1、開啟線程需要占用一定的內(nèi)存空間(默認(rèn)情況下,每一個(gè)線程都占512KB)
    2、如果開啟大量線程,會(huì)占用大量的內(nèi)存空間,降低程序的性能
    3、線程越多,CPU在線程調(diào)度上的開銷就越大
    4、程序設(shè)計(jì)更加復(fù)雜,比如線程間的通信、多線程的數(shù)據(jù)共享(比如2個(gè)人買同一張票)

iOS四種隊(duì)列

  • 串行隊(duì)列:
dispatch_queue_create("queue.name", DISPATCH_QUEUE_SERIAL);

所有任務(wù)按順序依次執(zhí)行,結(jié)束順序固定,符合先進(jìn)先出的基本原則,隊(duì)列后面的任務(wù)必須等待前面的任務(wù)執(zhí)行完畢后才出隊(duì)列。但是,不要認(rèn)為串行隊(duì)列中的所有任務(wù)都在同一個(gè)線程中執(zhí)行,串行隊(duì)列中的異步任務(wù),可能會(huì)開啟新線程去執(zhí)行。

  • 并發(fā)隊(duì)列
dispatch_queue_create("queue.name", DISPATCH_QUEUE_CONCURRENT);

所有任務(wù)可以同時(shí)執(zhí)行,結(jié)束順序不固定,只要有可用線程,則隊(duì)列頭部任務(wù)將持續(xù)出隊(duì)列。

  • 主隊(duì)列:
dispatch_get_main_queue()

本質(zhì)是一個(gè)特殊的串行隊(duì)列,主隊(duì)列的任務(wù)都在主線程來執(zhí)行,專門負(fù)責(zé)調(diào)度主線程度的任務(wù),無法開辟新的線程。所以,在主隊(duì)列下的任務(wù)不管是異步任務(wù)還是同步任務(wù)都不會(huì)開辟線程,任務(wù)只會(huì)在主線程順序執(zhí)行。如果在主線程上已經(jīng)有任務(wù)正在執(zhí)行,主隊(duì)列會(huì)等到主線程空閑后再調(diào)度任務(wù)。

  • 全局隊(duì)列
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

本質(zhì)是一個(gè)特殊的并發(fā)隊(duì)列。在后面加入了“服務(wù)質(zhì)量”和“調(diào)度優(yōu)先級” 兩個(gè)參數(shù)

線程與隊(duì)列的組合分析

  • 串行隊(duì)列+異步:順序執(zhí)行,先進(jìn)先出,可能會(huì)開啟新線程
//串行隊(duì)列
dispatch_queue_t serial = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
//異步任務(wù)
for (int i=0; i<5; i++) {

    dispatch_async(serial, ^{

        NSLog(@"這是第 %d 個(gè)任務(wù);線程 %@",i,[NSThread currentThread]);
        if (i==1 || i==3) {
            [NSThread sleepForTimeInterval:2];
        }
    });
}
NSLog(@"=============== 所有任務(wù)執(zhí)行完畢 =====");

執(zhí)行結(jié)果:
2022-05-17 15:27:35.976433+0800 myDemo[278:9179] =============== 所有任務(wù)執(zhí)行完畢 =====
2022-05-17 15:27:35.976538+0800 myDemo[278:9420] 這是第 0 個(gè)任務(wù);線程 <NSThread: 0x283628500>{number = 4, name = (null)}
2022-05-17 15:27:35.976655+0800 myDemo[278:9420] 這是第 1 個(gè)任務(wù);線程 <NSThread: 0x283628500>{number = 4, name = (null)}
2022-05-17 15:27:37.978072+0800 myDemo[278:9420] 這是第 2 個(gè)任務(wù);線程 <NSThread: 0x283628500>{number = 4, name = (null)}
2022-05-17 15:27:37.978150+0800 myDemo[278:9420] 這是第 3 個(gè)任務(wù);線程 <NSThread: 0x283628500>{number = 4, name = (null)}
2022-05-17 15:27:39.987439+0800 myDemo[278:9420] 這是第 4 個(gè)任務(wù);線程 <NSThread: 0x283628500>{number = 4, name = (null)}

  • 串行隊(duì)列+同步:順序執(zhí)行,先進(jìn)先出,不會(huì)開啟新線程
//串行隊(duì)列
dispatch_queue_t serial = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
//同步任務(wù)
for (int i=0; i<5; i++) {

    dispatch_sync(serial, ^{

        NSLog(@"這是第 %d 個(gè)任務(wù);線程 %@",i,[NSThread currentThread]);
        if (i==1 || i==3) {
            [NSThread sleepForTimeInterval:2];
        }
    });

執(zhí)行結(jié)果:

2022-05-17 15:14:04.059889+0800 myDemo[252:4951] 這是第 0 個(gè)任務(wù);線程 <NSThread: 0x282423f80>{number = 1, name = main}
2022-05-17 15:14:04.059996+0800 myDemo[252:4951] 這是第 1 個(gè)任務(wù);線程 <NSThread: 0x282423f80>{number = 1, name = main}
2022-05-17 15:14:06.060559+0800 myDemo[252:4951] 這是第 2 個(gè)任務(wù);線程 <NSThread: 0x282423f80>{number = 1, name = main}
2022-05-17 15:14:06.060716+0800 myDemo[252:4951] 這是第 3 個(gè)任務(wù);線程 <NSThread: 0x282423f80>{number = 1, name = main}
2022-05-17 15:14:08.061826+0800 myDemo[252:4951] 這是第 4 個(gè)任務(wù);線程 <NSThread: 0x282423f80>{number = 1, name = main}
  • 并發(fā)隊(duì)列+異步:同時(shí)執(zhí)行,每個(gè)任務(wù)的結(jié)束順序不確定,會(huì)開啟新線程
//并發(fā)隊(duì)列
dispatch_queue_t comp = dispatch_queue_create("comp", DISPATCH_QUEUE_CONCURRENT);
//異步任務(wù)
for (int i=0; i<5; i++) {

    dispatch_async(comp, ^{

        NSLog(@"這是第 %d 個(gè)任務(wù);線程 %@",i,[NSThread currentThread]);
        if (i==1 || i==3) {
            [NSThread sleepForTimeInterval:2];
        }
    });
}
NSLog(@"=============== 所有任務(wù)執(zhí)行完畢 =====");

執(zhí)行結(jié)果:
2022-05-17 15:39:06.309859+0800 myDemo[294:11951] =============== 所有任務(wù)執(zhí)行完畢 =====
2022-05-17 15:39:06.309962+0800 myDemo[294:12131] 這是第 0 個(gè)任務(wù);線程 <NSThread: 0x281719380>{number = 5, name = (null)}
2022-05-17 15:39:06.310064+0800 myDemo[294:12131] 這是第 1 個(gè)任務(wù);線程 <NSThread: 0x281719380>{number = 5, name = (null)}
2022-05-17 15:39:06.310165+0800 myDemo[294:12134] 這是第 2 個(gè)任務(wù);線程 <NSThread: 0x2817d2300>{number = 4, name = (null)}
2022-05-17 15:39:06.310236+0800 myDemo[294:12134] 這是第 3 個(gè)任務(wù);線程 <NSThread: 0x2817d2300>{number = 4, name = (null)}
2022-05-17 15:39:06.311765+0800 myDemo[294:12130] 這是第 4 個(gè)任務(wù);線程 <NSThread: 0x281719d40>{number = 6, name = (null)}

  • 并發(fā)隊(duì)列+同步:順序執(zhí)行,先進(jìn)先出,不會(huì)開啟新線程
//并發(fā)隊(duì)列
dispatch_queue_t comp = dispatch_queue_create("comp", DISPATCH_QUEUE_CONCURRENT);
//同步任務(wù)
for (int i=0; i<5; i++) {

    dispatch_sync(comp, ^{

        NSLog(@"這是第 %d 個(gè)任務(wù);線程 %@",i,[NSThread currentThread]);
        if (i==1 || i==3) {
            [NSThread sleepForTimeInterval:2];
        }
    });
}
NSLog(@"=============== 所有任務(wù)執(zhí)行完畢 =====");

執(zhí)行結(jié)果:
2022-05-17 15:37:08.804872+0800 myDemo[287:11040] 這是第 0 個(gè)任務(wù);線程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:08.804962+0800 myDemo[287:11040] 這是第 1 個(gè)任務(wù);線程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:10.805818+0800 myDemo[287:11040] 這是第 2 個(gè)任務(wù);線程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:10.806122+0800 myDemo[287:11040] 這是第 3 個(gè)任務(wù);線程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:12.807415+0800 myDemo[287:11040] 這是第 4 個(gè)任務(wù);線程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:12.807771+0800 myDemo[287:11040] =============== 所有任務(wù)執(zhí)行完畢 =====
  • 主隊(duì)列+異步:不會(huì)立即執(zhí)行,而是等待主隊(duì)列中的所有其他除我們添加到主隊(duì)列的任務(wù)都執(zhí)行完畢之后,才會(huì)執(zhí)行我們添加的任務(wù)
  • 主隊(duì)列+同步:會(huì)相互等待導(dǎo)致死鎖
//同步任務(wù)+主線程 == 相互等待,死鎖
for (int i=0; i<5; i++) {

        dispatch_sync(dispatch_get_main_queue(), ^{

        NSLog(@"這是第 %d 個(gè)任務(wù);線程 %@",i,[NSThread currentThread]);
        if (i==1 || i==3) {
            [NSThread sleepForTimeInterval:2];
        }
    });
}

iOS的四種代碼實(shí)現(xiàn)方式

pthread

C語言通用的多線程API,跨平臺,程序員手動(dòng)管理線程生命周期,使用難度大

NSThread

一個(gè) NSThread 對象就代表一條線程,使用較少

//先創(chuàng)建再啟動(dòng)線程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"jack"];
[thread start];

//直接創(chuàng)建并啟動(dòng)線程
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"jack"];

//直接創(chuàng)建并啟動(dòng)線程
[self performSelectorInBackground:@selector(run:) withObject:@"jack"];

//使線程進(jìn)入阻塞狀態(tài)
[NSThread sleepForTimeInterval:2.0];

GCD

GCD 是蘋果公司為多核的并行運(yùn)算提出的解決方案, GCD會(huì)自動(dòng)利用更多的 CPU 內(nèi)核(比如雙核、四核)來開啟線程執(zhí)行任務(wù),GCD 會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程),不需要我們程序員手動(dòng)管理內(nèi)存。

補(bǔ)充一個(gè)柵欄的示例代碼:
//并發(fā)隊(duì)列
dispatch_queue_t comp = dispatch_queue_create("comp", DISPATCH_QUEUE_CONCURRENT);
//異步任務(wù)
for (int i=0; i<5; i++) {

    dispatch_async(comp, ^{

        NSLog(@"這是第 %d 個(gè)任務(wù);線程 %@",i,[NSThread currentThread]);
        if (i==1 || i==3) {
            [NSThread sleepForTimeInterval:2];
        }
    });
}
    
dispatch_barrier_async(comp, ^{

    NSLog(@"這是第 dispatch_barrier_async 個(gè)任務(wù);線程 %@",[NSThread currentThread]);
});
NSLog(@"=============== 所有任務(wù)執(zhí)行完畢 =====");

執(zhí)行結(jié)果:
2022-05-17 16:17:57.471306+0800 myDemo[321:16764] =============== 所有任務(wù)執(zhí)行完畢 =====
2022-05-17 16:17:57.471536+0800 myDemo[321:16980] 這是第 1 個(gè)任務(wù);線程 <NSThread: 0x2825a7600>{number = 6, name = (null)}
2022-05-17 16:17:57.471645+0800 myDemo[321:16985] 這是第 0 個(gè)任務(wù);線程 <NSThread: 0x2825b83c0>{number = 5, name = (null)}
2022-05-17 16:17:57.471751+0800 myDemo[321:16985] 這是第 2 個(gè)任務(wù);線程 <NSThread: 0x2825b83c0>{number = 5, name = (null)}
2022-05-17 16:17:57.471894+0800 myDemo[321:16985] 這是第 3 個(gè)任務(wù);線程 <NSThread: 0x2825b83c0>{number = 5, name = (null)}
2022-05-17 16:17:57.472199+0800 myDemo[321:16979] 這是第 4 個(gè)任務(wù);線程 <NSThread: 0x282546580>{number = 3, name = (null)}
2022-05-17 16:17:59.495622+0800 myDemo[321:16985] 這是第 dispatch_barrier_async 個(gè)任務(wù);線程 <NSThread: 0x2825b83c0>{number = 5, name = (null)}

NSOperation、NSOperationQueue

NSOperation、NSOperationQueue 是蘋果提供給我們的一套多線程解決方案。實(shí)際上 NSOperation、NSOperationQueue 是基于 GCD 更高一層的封裝,完全面向?qū)ο?。但是?GCD 更簡單易用、代碼可讀性也更高。

//創(chuàng)建操作方法,啟動(dòng)
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationLoad:) object:@{@"obj":@"loadContent"}];
[invocationOperation start];
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    [self operationLoad:@{@"operation":@"11111111111"}];
}];
[operation start];
NSLog(@"invocationOperation/operation 開啟start命令");
//創(chuàng)建隊(duì)列
NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
NSBlockOperation *operationTwo = [NSBlockOperation blockOperationWithBlock:^{
    [self operationLoad:@{@"operation":@"2222222222"}];
}];
[opQueue addOperation:operationTwo];
//隊(duì)列直接添加block方法
[opQueue addOperationWithBlock:^{
    [self operationLoad:@{@"addOperationWithBlock":@"333333333"}];
}];
NSBlockOperation *operationFive = [NSBlockOperation blockOperationWithBlock:^{
    [self operationLoad:@{@"operation":@"5555555555"}];
}];
//隊(duì)列添加任務(wù)組
[opQueue addOperations:@[operationFive] waitUntilFinished:NO];
//添加?xùn)艡诠δ?if (@available(iOS 13.0, *)) {
    [opQueue addBarrierBlock:^{
        [self operationLoad:@{@"addBarrierBlock":@"888888888"}];
    }];
} else {
    // Fallback on earlier versions
}
    
NSLog(@"=============== 所有任務(wù)執(zhí)行完畢 =====");
    
執(zhí)行結(jié)果:
2022-05-17 16:54:34.193616+0800 myDemo[356:25036] 傳遞參數(shù)== {
    obj = loadContent;
} ,線程== <NSThread: 0x28147a9c0>{number = 1, name = main}
2022-05-17 16:54:36.196363+0800 myDemo[356:25036] 傳遞參數(shù)== {
    operation = 11111111111;
} ,線程== <NSThread: 0x28147a9c0>{number = 1, name = main}
2022-05-17 16:54:36.196762+0800 myDemo[356:25036] invocationOperation/operation 開啟start命令
2022-05-17 16:54:36.197310+0800 myDemo[356:25036] =============== 所有任務(wù)執(zhí)行完畢 =====
2022-05-17 16:54:38.321310+0800 myDemo[356:25232] 傳遞參數(shù)== {
    operation = 5555555555;
} ,線程== <NSThread: 0x281424e40>{number = 4, name = (null)}
2022-05-17 16:54:38.322240+0800 myDemo[356:25237] 傳遞參數(shù)== {
    addOperationWithBlock = 333333333;
} ,線程== <NSThread: 0x2814259c0>{number = 3, name = (null)}
2022-05-17 16:54:38.322340+0800 myDemo[356:25231] 傳遞參數(shù)== {
    operation = 2222222222;
} ,線程== <NSThread: 0x281824740>{number = 14, name = (null)}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

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

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