多線(xiàn)程相關(guān)概念
進(jìn)程與線(xiàn)程
進(jìn)程概念: 進(jìn)程是程序在計(jì)算機(jī)上的一次執(zhí)行活動(dòng)脊阴,打開(kāi)一個(gè)app披摄,就開(kāi)啟了一個(gè)進(jìn)程帅韧,可包含多個(gè)線(xiàn)程榨为。
線(xiàn)程概念: 獨(dú)立執(zhí)行的代碼段惨好,一個(gè)線(xiàn)程同時(shí)間只能執(zhí)行一個(gè)任務(wù),反之多線(xiàn)程并發(fā)就可以在同一時(shí)間執(zhí)行多個(gè)任務(wù)随闺。
iOS程序中日川,主線(xiàn)程(又叫作UI線(xiàn)程)主要任務(wù)是處理UI事件,顯示和刷新UI矩乐,(只有主線(xiàn)程有直接修改UI的能力)耗時(shí)的操作放在子線(xiàn)程(又叫作后臺(tái)線(xiàn)程龄句、異步線(xiàn)程)回论。在iOS中開(kāi)子線(xiàn)程去處理耗時(shí)的操作,可以有效提高程序的執(zhí)行效率分歇,提高資源利用率傀蓉。但是開(kāi)啟線(xiàn)程會(huì)占用一定的內(nèi)存,(主線(xiàn)程的堆棧大小是1M职抡,第二個(gè)線(xiàn)程開(kāi)始都是512KB葬燎,并且該值不能通過(guò)編譯器開(kāi)關(guān)或線(xiàn)程API函數(shù)來(lái)更改)降低程序的性能。所以一般不要同時(shí)開(kāi)很多線(xiàn)程缚甩。
線(xiàn)程相關(guān)
同步線(xiàn)程:同步線(xiàn)程會(huì)阻塞當(dāng)前線(xiàn)程去執(zhí)行線(xiàn)程內(nèi)的任務(wù)谱净,執(zhí)行完之后才會(huì)反回當(dāng)前線(xiàn)程。
異步線(xiàn)程:異步線(xiàn)程不會(huì)阻塞當(dāng)前線(xiàn)程擅威,會(huì)開(kāi)啟其他線(xiàn)程去執(zhí)行線(xiàn)程內(nèi)的任務(wù)壕探。
串行隊(duì)列:線(xiàn)程任務(wù)按先后順序逐個(gè)執(zhí)行(需要等待隊(duì)列里面前面的任務(wù)執(zhí)行完之后再執(zhí)行新的任務(wù))。
并發(fā)隊(duì)列:多個(gè)任務(wù)按添加順序一起開(kāi)始執(zhí)行(不用等待前面的任務(wù)執(zhí)行完再執(zhí)行新的任務(wù))郊丛,但是添加間隔往往忽略不計(jì)李请,所以看著像是一起執(zhí)行的。
并發(fā)VS并行:并行是基于多核設(shè)備的厉熟,并行一定是并發(fā)捻艳,并發(fā)不一定是并行。
多線(xiàn)程中會(huì)出現(xiàn)的問(wèn)題
Critical Section(臨界代碼段)
指的是不能同時(shí)被兩個(gè)線(xiàn)程訪(fǎng)問(wèn)的代碼段庆猫,比如一個(gè)變量认轨,被并發(fā)進(jìn)程訪(fǎng)問(wèn)后可能會(huì)改變變量值,造成數(shù)據(jù)污染(數(shù)據(jù)共享問(wèn)題)月培。
Race Condition (競(jìng)態(tài)條件)
當(dāng)多個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)共享的數(shù)據(jù)時(shí)嘁字,會(huì)發(fā)生爭(zhēng)用情形,第一個(gè)線(xiàn)程讀取改變了一個(gè)變量的值杉畜,第二個(gè)線(xiàn)程也讀取改變了這個(gè)變量的值纪蜒,兩個(gè)線(xiàn)程同時(shí)操作了該變量,此時(shí)他們會(huì)發(fā)生競(jìng)爭(zhēng)來(lái)看哪個(gè)線(xiàn)程會(huì)最后寫(xiě)入這個(gè)變量此叠,最后被寫(xiě)入的值將會(huì)被保留下來(lái)纯续。
Deadlock (死鎖)
兩個(gè)(多個(gè))線(xiàn)程都要等待對(duì)方完成某個(gè)操作才能進(jìn)行下一步,這時(shí)就會(huì)發(fā)生死鎖灭袁。
Thread Safe(線(xiàn)程安全)
一段線(xiàn)程安全的代碼(對(duì)象)猬错,可以同時(shí)被多個(gè)線(xiàn)程或并發(fā)的任務(wù)調(diào)度,不會(huì)產(chǎn)生問(wèn)題茸歧,非線(xiàn)程安全的只能按次序被訪(fǎng)問(wèn)倦炒。
所有Mutable對(duì)象都是非線(xiàn)程安全的,所有Immutable對(duì)象都是線(xiàn)程安全的软瞎,使用Mutable對(duì)象逢唤,一定要用同步鎖來(lái)同步訪(fǎng)問(wèn)(@synchronized)拉讯。
互斥鎖:能夠防止多線(xiàn)程搶奪造成的數(shù)據(jù)安全問(wèn)題,但是需要消耗大量的資源
原子屬性(atomic)加鎖
atomic: 原子屬性鳖藕,為setter方法加鎖魔慷,將屬性以atomic的形式來(lái)聲明,該屬性變量就能支持互斥鎖了著恩。
nonatomic: 非原子屬性盖彭,不會(huì)為setter方法加鎖,聲明為該屬性的變量页滚,客戶(hù)端應(yīng)盡量避免多線(xiàn)程爭(zhēng)奪同一資源。
Context Switch (上下文切換)
當(dāng)一個(gè)進(jìn)程中有多個(gè)線(xiàn)程來(lái)回切換時(shí)铺呵,context switch用來(lái)記錄執(zhí)行狀態(tài)裹驰,這樣的進(jìn)程和一般的多線(xiàn)程進(jìn)程沒(méi)有太大差別,但會(huì)產(chǎn)生一些額外的開(kāi)銷(xiāo)片挂。
多線(xiàn)程編程技術(shù)的優(yōu)缺點(diǎn)比較
NSThread (抽象層次:低)
優(yōu)點(diǎn):輕量級(jí)幻林,簡(jiǎn)單易用,可以直接操作線(xiàn)程對(duì)象
缺點(diǎn): 需要自己管理線(xiàn)程的生命周期音念,線(xiàn)程同步沪饺。線(xiàn)程同步對(duì)數(shù)據(jù)的加鎖會(huì)有一定的系統(tǒng)開(kāi)銷(xiāo)。
Cocoa NSOperation (抽象層次:中)
優(yōu)點(diǎn):不需要關(guān)心線(xiàn)程管理闷愤,數(shù)據(jù)同步的事情整葡,可以把精力放在學(xué)要執(zhí)行的操作上〖テ辏基于GCD遭居,是對(duì)GCD 的封裝,比GCD更加面向?qū)ο?br>
缺點(diǎn): NSOperation是個(gè)抽象類(lèi)旬渠,使用它必須使用它的子類(lèi)俱萍,可以實(shí)現(xiàn)它或者使用它定義好的兩個(gè)子類(lèi)NSInvocationOperation、NSBlockOperation.
GCD 全稱(chēng)Grand Center Dispatch (抽象層次:高)
優(yōu)點(diǎn):是 Apple 開(kāi)發(fā)的一個(gè)多核編程的解決方法告丢,簡(jiǎn)單易用枪蘑,效率高,速度快岖免,基于C語(yǔ)言岳颇,更底層更高效,并且不是Cocoa框架的一部分颅湘,自動(dòng)管理線(xiàn)程生命周期(創(chuàng)建線(xiàn)程赦役、調(diào)度任務(wù)、銷(xiāo)毀線(xiàn)程)栅炒。
缺點(diǎn): 使用GCD的場(chǎng)景如果很復(fù)雜掂摔,就有非常大的可能遇到死鎖問(wèn)題术羔。
GCD抽象層次最高,使用也簡(jiǎn)單乙漓,因此级历,蘋(píng)果也推薦使用GCD
GCD中的隊(duì)列類(lèi)型
GCD編程的核心就是dispatch隊(duì)列,dispatch block的執(zhí)行最終都會(huì)放進(jìn)某個(gè)隊(duì)列中去進(jìn)行叭披。
The main queue(主線(xiàn)程串行隊(duì)列): 與主線(xiàn)程功能相同寥殖,提交至Main queue的任務(wù)會(huì)在主線(xiàn)程中執(zhí)行,
Main queue 可以通過(guò)dispatch_get_main_queue()來(lái)獲取涩蜘。
Global queue(全局并發(fā)隊(duì)列): 全局并發(fā)隊(duì)列由整個(gè)進(jìn)程共享嚼贡,有高、中(默認(rèn))同诫、低粤策、后臺(tái)四個(gè)優(yōu)先級(jí)別。
Global queue 可以通過(guò)調(diào)用dispatch_get_global_queue函數(shù)來(lái)獲任蠼选(可以設(shè)置優(yōu)先級(jí))
Custom queue (自定義隊(duì)列): 可以為串行叮盘,也可以為并發(fā)。
Custom queue 可以通過(guò)dispatch_queue_create()來(lái)獲扰场柔吼;
Group queue (隊(duì)列組):將多線(xiàn)程進(jìn)行分組,最大的好處是可獲知所有線(xiàn)程的完成情況丙唧。
Group queue 可以通過(guò)調(diào)用dispatch_group_create()來(lái)獲取愈魏,通過(guò)dispatch_group_notify,可以直接監(jiān)聽(tīng)組里所有線(xiàn)程完成情況。
重點(diǎn)來(lái)了哈
GCD在項(xiàng)目中的實(shí)際應(yīng)用
GCD在項(xiàng)目中的實(shí)際應(yīng)用
GCD在項(xiàng)目中的實(shí)際應(yīng)用(一定要說(shuō)夠三遍O爰省r蚪洹!)
以下請(qǐng)求以afn為例
場(chǎng)景一
個(gè)頁(yè)面多個(gè)網(wǎng)絡(luò)請(qǐng)求沼琉,并且多個(gè)請(qǐng)求同時(shí)執(zhí)行北苟,都執(zhí)行完成以后進(jìn)行數(shù)據(jù)操作
實(shí)現(xiàn)方式1:dipatch_group
AFHTTPSessionManager *MANAGER = [AFHTTPSessionManager manager];
dispatch_group_enter(self.group);
[MANAGER POST:@"" parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
dispatch_group_leave(self.group);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
dispatch_group_leave(self.group);
}];
dispatch_group_enter(self.group);
[MANAGER POST:@"" parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
dispatch_group_leave(self.group);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
dispatch_group_leave(self.group);
}];
dispatch_group_notify(self.group, self.chquue, ^{
NSLog(@"11111");
});
其他類(lèi)似代碼實(shí)現(xiàn)相同效果
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t conQueue = dispatch_queue_create("fsfsdfsf2", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t seQueue = dispatch_queue_create("asdsadasdasda", NULL);
dispatch_queue_t seQueue1 = dispatch_queue_create("asdsadasasdadassda", NULL);
//
//
dispatch_group_enter(group);
dispatch_async(conQueue, ^{
sleep(6);
NSLog(@"111111");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(seQueue, ^{
sleep(2);
NSLog(@"222222");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(seQueue1, ^{
sleep(4);
NSLog(@"33333");
dispatch_group_leave(group);
});
//
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"444444");
});
//
NSLog(@"hhahahaha");
其中的隊(duì)列可以是不同的隊(duì)列,只要group是同一個(gè)打瘪,都會(huì)得到最后執(zhí)行notify代碼的效果友鼻,如果是同一個(gè)串行隊(duì)列,則會(huì)按照1-2-3-4的順序串行執(zhí)行以上4個(gè)任務(wù)
實(shí)現(xiàn)方式2:dispatch_semaphore
dispatch_async(self.chquue, ^{
dispatch_semaphore_t SEM = dispatch_semaphore_create(0);
AFHTTPSessionManager *MANAGER = [AFHTTPSessionManager manager];
[MANAGER POST:@"" parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
dispatch_semaphore_signal(SEM);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
dispatch_semaphore_signal(SEM);
}];
dispatch_semaphore_t SEM2 = dispatch_semaphore_create(0);
[MANAGER POST:@"" parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
dispatch_semaphore_signal(SEM2);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
dispatch_semaphore_signal(SEM2);
}];
dispatch_semaphore_wait(SEM, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(SEM2, DISPATCH_TIME_FOREVER);
NSLog(@"hahaha");
});
NSLog(@"hello");
此處為什么要套一個(gè)dispatch_ASYNC呢闺骚?這樣做是為了防止阻塞主線(xiàn)程的任務(wù)執(zhí)行彩扔!
場(chǎng)景二
一個(gè)頁(yè)面多個(gè)網(wǎng)絡(luò)請(qǐng)求,并且多個(gè)請(qǐng)求串執(zhí)行僻爽,下一個(gè)請(qǐng)求需要等待上一個(gè)的請(qǐng)求結(jié)果才能繼續(xù)請(qǐng)求
實(shí)現(xiàn)方式1:dipatch_group虫碉,如何使用group的方式來(lái)實(shí)現(xiàn)這個(gè)需求,歡迎大神提出意見(jiàn)
實(shí)現(xiàn)方式2:dispatch_semaphore
dispatch_async(self.chquue, ^{
dispatch_semaphore_t SEM = dispatch_semaphore_create(0);
AFHTTPSessionManager *MANAGER = [AFHTTPSessionManager manager];
[MANAGER POST:@"" parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
dispatch_semaphore_signal(SEM);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
dispatch_semaphore_signal(SEM);
}];
dispatch_semaphore_wait(SEM, DISPATCH_TIME_FOREVER);
[MANAGER POST:@"" parameters:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
dispatch_semaphore_signal(SEM);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
dispatch_semaphore_signal(SEM);
}];
dispatch_semaphore_wait(SEM, DISPATCH_TIME_FOREVER);
NSLog(@"hahaha");
});
NSLog(@"hello");
這段代碼的關(guān)鍵點(diǎn)是只使用一個(gè)sem來(lái)實(shí)現(xiàn)串行執(zhí)行異步任務(wù)
當(dāng)然如果哪位大神有更好的實(shí)現(xiàn)方式歡迎指點(diǎn)P匕稹敦捧!
場(chǎng)景三
死鎖
dispatch_queue_t serialQueue = dispatch_queue_create("com.dullgrass.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
NSLog(@"會(huì)執(zhí)行的代碼");
//當(dāng)嵌套的這個(gè)任務(wù)是同步且在串行隊(duì)列中執(zhí)行時(shí)须板,就會(huì)造成死鎖
dispatch_sync(serialQueue, ^{
NSLog(@"代碼不執(zhí)行");
});
});
關(guān)于dispatc_group
dispatch_enter和dispatch_leave要成對(duì)出現(xiàn),否則奔潰兢卵。
補(bǔ)充
關(guān)于dispatch_set_target_queue
場(chǎng)景一
使用dispatch_set_target_queue更改Dispatch Queue的執(zhí)行優(yōu)先級(jí)
dispatch_queue_create函數(shù)生成的DisPatch Queue不管是Serial DisPatch Queue還是Concurrent Dispatch Queue,執(zhí)行的優(yōu)先級(jí)都與默認(rèn)優(yōu)先級(jí)的Global Dispatch queue相同,如果需要變更生成的Dispatch Queue的執(zhí)行優(yōu)先級(jí)則需要使用dispatch_set_target_queue函數(shù)
- (void)testTeagerQueue1 {
2 dispatch_queue_t serialQueue = dispatch_queue_create("com.oukavip.www",NULL);
3 dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0);
4
5 dispatch_set_target_queue(serialQueue, globalQueue);
6 // 第一個(gè)參數(shù)為要設(shè)置優(yōu)先級(jí)的queue,第二個(gè)參數(shù)是參照物习瑰,既將第一個(gè)queue的優(yōu)先級(jí)和第二個(gè)queue的優(yōu)先級(jí)設(shè)置一樣。
7 }
場(chǎng)景二
使用dispatch_set_target_queue將多個(gè)串行的queue指定到了同一目標(biāo)秽荤,那么著多個(gè)串行queue在目標(biāo)queue上就是同步執(zhí)行的甜奄,不再是并行執(zhí)行。
- (void)testTargetQueue {
//1.創(chuàng)建目標(biāo)隊(duì)列
dispatch_queue_t targetQueue = dispatch_queue_create("test.target.queue", DISPATCH_QUEUE_SERIAL);
//2.創(chuàng)建3個(gè)串行隊(duì)列
dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);
//3.將3個(gè)串行隊(duì)列分別添加到目標(biāo)隊(duì)列
dispatch_set_target_queue(queue1, targetQueue);
dispatch_set_target_queue(queue2, targetQueue);
dispatch_set_target_queue(queue3, targetQueue);
dispatch_async(queue1, ^{
NSLog(@"1 in");
[NSThread sleepForTimeInterval:3.f];
NSLog(@"1 out");
});
dispatch_async(queue2, ^{
NSLog(@"2 in");
[NSThread sleepForTimeInterval:2.f];
NSLog(@"2 out");
});
dispatch_async(queue3, ^{
NSLog(@"3 in");
[NSThread sleepForTimeInterval:1.f];
NSLog(@"3 out");
});
}
以上是我在工作中遇到的關(guān)于gcd的一些總結(jié)窃款,歡迎各路大神補(bǔ)充课兄,共同進(jìn)步!晨继!如果您看過(guò)文章覺(jué)得用得上烟阐,請(qǐng)給個(gè)贊謝謝!