線程與進(jìn)程
線程
- 1.
線程是進(jìn)程的基本執(zhí)?單元
钟鸵,?個(gè)進(jìn)程的所有任務(wù)都在線程中執(zhí)?
- 2.
進(jìn)程要想執(zhí)?任務(wù)晚伙,必須得有線程
拾弃,進(jìn)程?少要有?條線程
- 3.程序啟動(dòng)會(huì)
默認(rèn)開啟?條線程
,這條線程被稱為主線程
或 UI 線程
進(jìn)程
- 1.進(jìn)程是指在
系統(tǒng)中正在運(yùn)?的?個(gè)應(yīng)?程序
- 2.
每個(gè)進(jìn)程之間是獨(dú)?的
丽旅,每個(gè)進(jìn)程均運(yùn)?在其專?的且受保護(hù)的內(nèi)存空間內(nèi) - 3.通過“活動(dòng)監(jiān)視器”可以查看 Mac 系統(tǒng)中所開啟的進(jìn)程
線程和進(jìn)程的聯(lián)系
- 1.
地址空間
:同?進(jìn)程的線程共享本進(jìn)程的地址空間
,?進(jìn)程之間則是獨(dú)?的地址空間
纺棺。 - 2.
資源擁有
:同?進(jìn)程內(nèi)的線程共享本進(jìn)程的資源
如內(nèi)存榄笙、I/O、cpu等祷蝌,但是進(jìn)程之間的資源是獨(dú)?的
茅撞。- 1:
?個(gè)進(jìn)程崩潰后,在保護(hù)模式下不會(huì)對(duì)其他進(jìn)程產(chǎn)?影響巨朦,但是?個(gè)線程崩潰整個(gè)進(jìn)程都死掉米丘。所以多進(jìn)程要?多線程健壯
。 - 2:
進(jìn)程切換時(shí)罪郊,消耗的資源?蠕蚜,效率?。所以涉及到頻繁的切換時(shí)悔橄,使?線程要好于進(jìn)程
靶累。同樣如果要求同時(shí)進(jìn)?并且?要共享某些變量的并發(fā)操作腺毫,只能?線程不能?進(jìn)程
- 3: 執(zhí)?過程:
每個(gè)獨(dú)?的進(jìn)程有?個(gè)程序運(yùn)?的??、順序執(zhí)?序列和程序??
挣柬。但是線程不能獨(dú)?執(zhí)?潮酒,必須依存在應(yīng)?程序中,由應(yīng)?程序提供多個(gè)線程執(zhí)?控制
邪蛔。 - 4:
線程是處理器調(diào)度的基本單位急黎,但是進(jìn)程不是
。 - 5:
線程沒有地址空間,線程包含在進(jìn)程地址空間中
侧到。
- 1:
多線程的優(yōu)點(diǎn)和缺點(diǎn)
優(yōu)點(diǎn)
- 能適當(dāng)
提?程序的執(zhí)?效率
- 能適當(dāng)
- 能適當(dāng)
提?資源的利?率
(CPU勃教,內(nèi)存)
- 能適當(dāng)
- 線程上的任務(wù)執(zhí)?完成后,
線程會(huì)?動(dòng)銷毀
- 線程上的任務(wù)執(zhí)?完成后,
缺點(diǎn)
- 開啟線程需要
占??定的內(nèi)存空間
(默認(rèn)情況下匠抗,每?個(gè)線程都占 512 KB)
- 開啟線程需要
- 如果
開啟?量的線程故源,會(huì)占??量的內(nèi)存空間
,降低程序的性能
- 如果
- 3.
線程越多汞贸,CPU 在調(diào)?線程上的開銷就越?
- 4.
程序設(shè)計(jì)更加復(fù)雜
绳军,?如線程間的通信、多線程的數(shù)據(jù)共享
多線程的實(shí)際實(shí)現(xiàn)
先介紹一個(gè)概念:時(shí)間片矢腻。時(shí)間片:CPU在多個(gè)任務(wù)之間進(jìn)行快速切換门驾,這個(gè)時(shí)間間隔就是時(shí)間片。
單核和多核
- 1.單核:同一時(shí)間多柑,
CPU只能處理一個(gè)線程
奶是,也就是說同一時(shí)間只有一個(gè)線程在執(zhí)行。 - 2.多線程同時(shí)執(zhí)行:
CPU快速的在多個(gè)線程之間切換
顷蟆。CPU調(diào)度線程的時(shí)間足夠快诫隅,就造成了多線程'同時(shí)'執(zhí)行的效果 - 3.線程非常多:
CPU會(huì)在N個(gè)線程之間切換,消耗大量的CPU資源
帐偎,每個(gè)線程的被調(diào)度次數(shù)會(huì)降低逐纬,線程的執(zhí)行效率降低
多線程的內(nèi)存消耗
官方文檔對(duì)多線程的內(nèi)存消耗有個(gè)說明官方文檔傳送門
- 1.線程
內(nèi)核數(shù)據(jù)結(jié)構(gòu)大約占1KB
,主要用于存儲(chǔ)線程數(shù)據(jù)結(jié)構(gòu)和屬性
削樊,其中大部分是作為連接內(nèi)存分配
的豁生,因此不能分頁到磁盤
。 - 2.棧的控件大新辍:
子線程512kb甸箱,OS X的主線程是8MB,iOS的主線程是1MB
迅脐。子線程允許的最小堆棧大小是16 KB
芍殖,堆棧大小必須是4 KB的倍數(shù)
。該內(nèi)存的空間是在線程創(chuàng)建時(shí)在進(jìn)程空間中留出的谴蔑,但是只有在需要時(shí)才創(chuàng)建與該內(nèi)存關(guān)聯(lián)的實(shí)際頁面豌骏。 - 3.
創(chuàng)建時(shí)間大概90微秒
龟梦,這個(gè)值反映了從創(chuàng)建線程的初始調(diào)用到線程入口點(diǎn)例程開始執(zhí)行的時(shí)間之間的時(shí)間
。這些數(shù)字是通過分析在運(yùn)行OS X v10.5窃躲、配置2 GHz雙核處理器和1 GB RAM计贰、基于intel的iMac上創(chuàng)建線程時(shí)生成的平均值和中值得出的。
多線程處理方案
線程生命周期
線程池的原理
飽和策略
- 1.AbortPolicy
直接拋出RejectedExecutionExeception異常來阻?系統(tǒng)正常運(yùn)?
- 2.CallerRunsPolicy
將任務(wù)回退到調(diào)?者
- 3.DisOldestPolicy
丟掉等待最久的任務(wù)
- 4.DisCardPolicy
直接丟棄任務(wù)
這四種拒絕策略均實(shí)現(xiàn)了RejectedExecutionHandler接?
線程優(yōu)先級(jí)
地址越高蒂窒,其優(yōu)先級(jí)越高躁倒,也就是說用戶操作行為的優(yōu)先級(jí)是最高的。但注意:
- 1.
優(yōu)先級(jí)越高洒琢,執(zhí)行速度不一定越快
秧秉,跟資源大小
(任務(wù)復(fù)雜度)和CPU的調(diào)度
(多任務(wù)) - 2.
多任務(wù)就會(huì)出現(xiàn)資源搶奪問題
(會(huì)導(dǎo)致數(shù)據(jù)出錯(cuò)),此時(shí)需要鎖來防止這種情況出現(xiàn)
鎖
鎖作用
- 1.
保證鎖內(nèi)的代碼衰抑,同?時(shí)間福贞,只有?條線程能夠執(zhí)?
! - 2.
鎖的鎖定范圍停士,應(yīng)該盡量?,鎖定范圍越?完丽,效率越差
恋技!
鎖使用的注意點(diǎn)
- 1.能夠加鎖的
任意 NSObject 對(duì)象
- 2.注意:鎖對(duì)象?定要保證
所有的線程都能夠訪問
- 3.如果代碼中只有?個(gè)地?需要加鎖,?多都使? self逻族,這樣可以避免單獨(dú)再創(chuàng)建?個(gè)鎖對(duì)象
互斥鎖和自旋鎖
自旋鎖
自旋鎖成員
自旋鎖包含:atomic, OSSpinLock, dispatch_semaphore_t
等
定義:
定義:是一種用于保護(hù)多線程共享資源的鎖
蜻底,與一般互斥鎖(mutex)不同之處在于當(dāng)自旋鎖嘗試獲取鎖時(shí)以忙等待(busy waiting)的形式不斷地循環(huán)檢查鎖是否可用
。當(dāng)上一個(gè)線程的任務(wù)沒有執(zhí)行完畢的時(shí)候(被鎖住
)聘鳞,那么下一個(gè)線程會(huì)一直等待(不會(huì)睡眠)
薄辅,當(dāng)上一個(gè)線程的任務(wù)執(zhí)行完畢,下一個(gè)線程會(huì)立即執(zhí)行
抠璃。
自旋鎖會(huì)忙等: 所謂忙等站楚,即在訪問被鎖資源時(shí),調(diào)用者線程不會(huì)休眠
搏嗡,而是不停循環(huán)在那里
窿春,直到被鎖資源釋放鎖。
使用場(chǎng)景
- 1.
對(duì)持有鎖較短的程序來說采盒,使用自旋鎖代替一般的互斥鎖往往能夠提高程序的性能
旧乞。 - 2.
加鎖代碼(臨界區(qū))經(jīng)常被調(diào)用,但競(jìng)爭(zhēng)情況很少發(fā)生
- 3.
CPU資源不緊張
- 4.
多核處理器
互斥鎖
互斥鎖成員
互斥鎖包含:@synchronized磅氨,NSLock, pthread_mutex, NSConditionLock, NSCondition, NSRecursiveLock
等
定義
定義:互斥鎖當(dāng)上一個(gè)線程的任務(wù)沒有執(zhí)行完畢的時(shí)候(被鎖壮咂堋),那么下一個(gè)線程會(huì)進(jìn)入睡眠狀態(tài)等待任務(wù)執(zhí)行完畢
烦租,當(dāng)上一個(gè)線程的任務(wù)執(zhí)行完畢延赌,下一個(gè)線程會(huì)自動(dòng)喚醒然后執(zhí)行任務(wù)除盏。
互斥鎖會(huì)休眠: 所謂休眠,即在訪問被鎖資源時(shí)皮胡,調(diào)用者線程會(huì)休眠
痴颊,此時(shí)cpu可以調(diào)度其他線程工作
。直到被鎖資源釋放鎖屡贺。此時(shí)會(huì)喚醒休眠線程蠢棱。
使用場(chǎng)景
- 1.
預(yù)計(jì)線程等待鎖的時(shí)間較長(zhǎng)
- 2.
單核處理器
- 3.
臨界區(qū)有IO操作
- 4.
臨界區(qū)代碼復(fù)雜或者循環(huán)量大
- 5.
臨界區(qū)競(jìng)爭(zhēng)非常激烈
atomic與nonatomic 的區(qū)別
nonatomic ?原?屬性
- 1.nonatomic:
?線程安全
,適合內(nèi)存?
的移動(dòng)設(shè)備
atomic 原?屬性
- 1.讀取值線程安全
- 2..atomic:
讀寫安全甩栈,需要消耗?量的資源
(至少是nonatomic的10倍)
說明:使用atomic修飾屬性時(shí)泻仙,編譯器在生成getter和setter方法時(shí),在getter和setter方法內(nèi)部實(shí)現(xiàn)進(jìn)行加鎖操作
量没,這么做的目的是為了保證屬性讀寫的安全性和完整性
玉转,也就是說對(duì)于屬性值得存取是線程安全
的。但這個(gè)不能保證操作這個(gè)屬性的時(shí)候是線程安全的
殴蹄。
iOS 開發(fā)的建議
- 1.
所有屬性都聲明為 nonatomic
- 2.盡量
避免多線程搶奪同?塊資源
究抓,盡量將加鎖
、資源搶奪的業(yè)務(wù)邏輯交給服務(wù)器端處理袭灯,減?移動(dòng)客戶端的壓?
線程和Runloop的關(guān)系
- 1.
runloop與線程是??對(duì)應(yīng)的
刺下,?個(gè)runloop對(duì)應(yīng)?個(gè)核?的線程
,為什么說是核?的稽荧,是因?yàn)?code>runloop是可以嵌套的橘茉,但是核?的只能有?個(gè),他們的關(guān)系保存在?個(gè)全局的字典?
姨丈。 - 2.
runloop是來管理線程的
畅卓,當(dāng)線程的runloop被開啟后,線程會(huì)在執(zhí)?完任務(wù)后進(jìn)?休眠狀態(tài)
蟋恬,有了任務(wù)就會(huì)被喚醒去執(zhí)?任務(wù)
翁潘。 - 3.
runloop在第?次獲取時(shí)被創(chuàng)建,在線程結(jié)束時(shí)被銷毀
筋现。 - 4.對(duì)于
主線程
來說唐础,runloop在程序?啟動(dòng)就默認(rèn)創(chuàng)建好了
。 - 5.對(duì)于
?線程來
說矾飞,runloop是懶加載的
一膨,只有當(dāng)我們使?的時(shí)候才會(huì)創(chuàng)建
,所以在?線程?定時(shí)器
要注意:確保?線程的runloop被創(chuàng)建
洒沦,不然定時(shí)器不會(huì)回調(diào)豹绪。
延伸
端口通訊:NSPort
下面我們舉例子來展示多線程的端口通訊,通過KCPerson對(duì)象跟ViewController進(jìn)行發(fā)送消息的例子
// ViewController.m
//1\. 創(chuàng)建主線程的port
// 子線程通過此端口發(fā)送消息給主線程
self.myPort = [NSMachPort port];
//2\. 設(shè)置port的代理回調(diào)對(duì)象
self.myPort.delegate = self;
//3\. 把port加入runloop,接收port消息
[[NSRunLoop currentRunLoop] addPort:self.myPort forMode:NSDefaultRunLoopMode];
self.person = [[KCPerson alloc] init];
[NSThread detachNewThreadSelector:@selector(personLaunchThreadWithPort:)
toTarget:self.person
withObject:self.myPort];
- (void)handlePortMessage:(NSPortMessage *)message {
NSLog(@"VC == %@",[NSThread currentThread]);
NSArray *messageArr = [message valueForKey:@"components"];
NSString *dataStr = [[NSString alloc] initWithData:messageArr.firstObject encoding:NSUTF8StringEncoding];
NSLog(@"傳過來一些信息 :%@",dataStr);
NSPort *destinPort = [message valueForKey:@"remotePort"];
if(!destinPort || ![destinPort isKindOfClass:[NSPort class]]){
NSLog(@"傳過來的數(shù)據(jù)有誤");
return;
}
NSData *data = [@"VC收到!!!" dataUsingEncoding:NSUTF8StringEncoding];
NSMutableArray *array =[[NSMutableArray alloc]initWithArray:@[data,self.myPort]];
// 非常重要,如果你想在Person的port接受信息,必須加入到當(dāng)前主線程的runloop
[[NSRunLoop currentRunLoop] addPort:destinPort forMode:NSDefaultRunLoopMode];
NSLog(@"VC == %@",[NSThread currentThread]);
BOOL success = [destinPort sendBeforeDate:[NSDate date]
msgid:10010
components:array
from:self.myPort
reserved:0];
NSLog(@"%d",success);
}
// Person.m
- (void)personLaunchThreadWithPort:(NSPort *)port{
NSLog(@"VC 響應(yīng)了Person里面");
@autoreleasepool {
//1\. 保存主線程傳入的port
self.vcPort = port;
//2\. 設(shè)置子線程名字
[[NSThread currentThread] setName:@"KCPersonThread"];
//3\. 開啟runloop
[[NSRunLoop currentRunLoop] run];
//4\. 創(chuàng)建自己port
self.myPort = [NSMachPort port];
//5\. 設(shè)置port的代理回調(diào)對(duì)象
self.myPort.delegate = self;
//6\. 完成向主線程port發(fā)送消息
[self sendPortMessage];
}
}
/**
* 完成向主線程發(fā)送port消息
*/
- (void)sendPortMessage {
NSData *data1 = [@"AAAA" dataUsingEncoding:NSUTF8StringEncoding];
NSData *data2 = [@"BBBB" dataUsingEncoding:NSUTF8StringEncoding];
NSMutableArray *array =[[NSMutableArray alloc]initWithArray:@[data1,self.myPort]];
// 發(fā)送消息到VC的主線程
// 第一個(gè)參數(shù):發(fā)送時(shí)間瞒津。
// msgid 消息標(biāo)識(shí)蝉衣。
// components,發(fā)送消息附帶參數(shù)巷蚪。
// reserved:為頭部預(yù)留的字節(jié)數(shù)
[self.vcPort sendBeforeDate:[NSDate date]
msgid:10086
components:array
from:self.myPort
reserved:0];
}
- (void)handlePortMessage:(NSPortMessage *)message{
NSLog(@"person:handlePortMessage == %@",[NSThread currentThread]);
NSLog(@"從VC 傳過來一些信息:");
NSLog(@"components == %@",[message valueForKey:@"components"]);
NSLog(@"receivePort == %@",[message valueForKey:@"receivePort"]);
NSLog(@"sendPort == %@",[message valueForKey:@"sendPort"]);
NSLog(@"msgid == %@",[message valueForKey:@"msgid"]);
}
運(yùn)行打硬≌薄: 注意:
- 1.NSPort對(duì)象
必須添加到要接受的線程的runLoop中
- 2.
接收消息的對(duì)象實(shí)現(xiàn)NSPortDelegate協(xié)議的-handlePortMessage:方法來獲取消息內(nèi)容