進程\線程
-
進程
- 進程 是指在系統(tǒng)中正在運行的一個應(yīng)用程序。
- 每個進程之間是獨立的点把,每個進程均運行在其專用的且受保護的內(nèi)存空間內(nèi)橘荠。
-
線程
- 1個進程由多個線程組成(1個進程至少要有1個線程)。
- 線程是進程的基本執(zhí)行單元郎逃,一個進程的所有任務(wù)都在線程中執(zhí)行哥童。
多線程
-
多線程
- 1個進程中可以開啟多個線程,多個線程可以同時執(zhí)行不同的任務(wù)褒翰。
- 多線程可以提高程序的執(zhí)行效率贮懈。
-
多線程的原理
- 對于單核CPU來說,同一時間优训,CPU只能處理1個線程朵你,只有1個線程正在執(zhí)行。
- 多線程同時執(zhí)行的本質(zhì):是CPU快速的在多個線程之間的切換揣非。
- CPU調(diào)度線程的時間足夠快抡医,就造成了多線程的“同時”執(zhí)行。
- 如果線程數(shù)非常多早敬,CPU會在n個線程之間切換魂拦,消耗大量的CPU資源毛仪,每個線程被調(diào)度的次數(shù)會降低,線程的執(zhí)行效率降低芯勘。
-
多線程的優(yōu)點
- 能適當(dāng)提高程序的執(zhí)行效率箱靴。
- 能適當(dāng)提高資源的利用率(CPU、內(nèi)存)荷愕。
- 線程上的任務(wù)執(zhí)行完成后衡怀,線程會自動銷毀。
-
多線程的缺點
- 開啟線程需要占用一定的內(nèi)存空間(默認情況下安疗,每一個線程都占用512KB)抛杨,如果開啟大量的線程,會占用大量的內(nèi)存空間荐类,降低程序的性能怖现。
- 線程越多,CPU在調(diào)用線程上的開銷就越大玉罐。
- 程序設(shè)計更加復(fù)雜屈嗤,比如線程間的通信、多線程的數(shù)據(jù)共享吊输。
-
主線程
- 一個程序運行后饶号,默認會開啟1個線程,稱為“主線程”或“UI線程”季蚂。
- 主線程一般用來刷新UI界面茫船,處理UI事件。
- 主線程使用注意:別將耗時的操作放到主線程中扭屁,因為耗時操作會卡住主線程算谈,嚴重影響UI的流暢度,給用戶一種卡的壞體驗料滥。
同步和異步
-
同步執(zhí)行
- 寫程序的時候都是從上到下然眼,從左到右,代碼執(zhí)行順序也是從上到下從右到左幔欧。
- 1個線程執(zhí)行多個任務(wù),也是依次執(zhí)行a->b->c丽声。
- 1個線程同一時間執(zhí)行1個任務(wù)礁蔗。
-
異步執(zhí)行
- 多個線程可以同時執(zhí)行多個任務(wù)。
- 多個線程執(zhí)行多個任務(wù)可以同時執(zhí)行a雁社,b浴井,c。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// 異步執(zhí)行
[self performSelectorInBackground:@selector(test) withObject:nil];
NSLog(@"over");
}
// 演示耗時操作
- (void)test {
for (int i = 0; i < 1000 ; i++) {
// 操作椕鼓欤空間 速度特別快
// int n = 10;
// 操作堆空間磺浙,速度慢
// NSString *str = [NSString stringWithFormat:@"abc %d",i];
// io操作 input output
NSLog(@"abc");
}
}
iOS中多線程的實現(xiàn)方案
-
技術(shù)方案
- pthread洪囤,C語言的一套通用的多線程API,適用于UNIX\Linux\Windows等系統(tǒng)撕氧,跨平臺\可移植瘤缩,使用難度大,線程生命周期需要程序員管理伦泥。
- NSThread剥啤,OC語言,使用更加面向?qū)ο蟛桓唵我子酶樱芍苯硬僮骶€程對象,線程生命周期需要程序員管理防楷。
- GCD牺丙,C語言,旨在替代NSThread等線程技術(shù)复局,充分利用設(shè)備的多核冲簿,線程生命周期自動管理。
- NSOperation肖揣,基于OC語言(底層是GCD)民假,比GCD多了一些更簡單實用的功能,使用更加面向?qū)ο罅牛€程生命周期自動管理羊异。
-
pthread的使用
- 使用pthread需要導(dǎo)入頭文件
#import <pthread.h>
。 - 導(dǎo)入的頭文件是跨平臺的彤断,都是C語言的野舶。
- 通過
intpthread_create()
函數(shù)創(chuàng)建線程。 - 在ARC中宰衙,使用到和C語言對應(yīng)的數(shù)據(jù)類型平道,應(yīng)該使用
__bridge
橋接,在MRC中不需要橋接供炼。在OC中一屋,如果是ARC的話,編譯的時候會自動添加retain袋哼、release冀墨、autorelease,ARC只負責(zé)OC的代碼,不負責(zé)C的代碼,如果C語言的框架中篷就,出現(xiàn)create箩做、retain虫腋、copy骄酗,需要release,而橋接的目的就是解決這個問題悦冀。
- 使用pthread需要導(dǎo)入頭文件
int pthread_create(pthread_t * __restrict,
const pthread_attr_t * __restrict,
void *(*)(void *),
void * __restrict);
// 參數(shù):
// 第1個參數(shù)為指向線程標示符的指針(線程編號的地址)
// 第2個參數(shù)用來設(shè)置線程屬性
// void *(*)(void *) 返回值 (函數(shù)指針)(參數(shù))
// void * 返回值類型趋翻,類似于OC中的id
// (*) 函數(shù)的指針
// (void *) 參數(shù)列表
// 第3個參數(shù)是線程調(diào)用函數(shù)指針
// 第4個參數(shù)是運行函數(shù)的參數(shù)
// 返回值:
// 返回0 線程創(chuàng)建成功 此時第1個參數(shù)是新線程的編號
// 非0 線程創(chuàng)建失敗 返回對應(yīng)錯誤碼
pthread的使用
- NSThread
- NSThread線程的執(zhí)行是異步的。
- 面向?qū)ο蟮姆绞絼?chuàng)建子線程雏门。
// 通過NSThread開啟1個子線程的3種方式
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument;
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;
NSThread的使用
- 線程的狀態(tài)
- 當(dāng)啟動線程后嘿歌,線程的方法并沒有立即執(zhí)行,而是進入就緒狀態(tài)茁影,等待CPU的調(diào)用宙帝。
- 線程的狀態(tài)分為新生狀態(tài)、就緒狀態(tài)募闲、運行狀態(tài)步脓、死亡狀態(tài)、阻塞狀態(tài)浩螺。
- 終止線程前靴患,要釋放之前分配的內(nèi)存。如果是ARC要出,需要清理C語言框架創(chuàng)建的對象鸳君,否則會出現(xiàn)內(nèi)存泄露。
線程的狀態(tài)
- 線程常用屬性
- 線程名稱:設(shè)置線程名稱可以當(dāng)線程執(zhí)行的方法內(nèi)部出現(xiàn)異常的時候記錄異常和當(dāng)前線程患蹂。
- 線程優(yōu)先級:內(nèi)核調(diào)度算法在決定該運行哪個線程時或颊,會把線程的優(yōu)先級作為考量因素,較高優(yōu)先級的線程會比較低優(yōu)先級的線程具有更多的運行機會传于。較高優(yōu)先級不保證你的線程具有執(zhí)行的時間囱挑,只是相比較低優(yōu)先級的線程,它更有可能被調(diào)度器選擇執(zhí)行而已沼溜。
線程的屬性
多線程訪問共享資源的問題
- 互斥鎖(線程同步)
- 如果多個線程同時讀寫一個公共變量(屬性)平挑,可能會出現(xiàn)問題。
- 為解決上面的問題系草,在代碼中加入互斥鎖(線程同步):
- 互斥鎖保證鎖內(nèi)的代碼同一時間通熄,只有一個線程能夠執(zhí)行鎖住的代碼塊。
- 互斥鎖的參數(shù)必須是一個對象找都,任意一個對象都可以唇辨,但是不能是線程執(zhí)行方法中定義的對象,一般用self即可檐嚣。
- 互斥鎖的原理:每一個對象(NSObject)內(nèi)部都有一個鎖(變量)助泽,當(dāng)有線程要進入@synchronized到代碼塊中會先檢查對象的鎖是打開還是關(guān)閉狀態(tài),默認鎖是打開狀態(tài)(1)嚎京,如果是線程執(zhí)行到代碼塊內(nèi)部會先上鎖(0)嗡贺。如果鎖被關(guān)閉,再有線程要執(zhí)行代碼塊就先等待鞍帝,直到鎖打開才可以進入诫睬。
- 加鎖后程序執(zhí)行的效率比不加鎖的時候要低,因為線程要等待鎖帕涌,但是鎖保證了多個線程同時操作全局變量的安全性摄凡。
線程執(zhí)行到@synchronized
① 檢查鎖的狀態(tài) 如果是開鎖狀態(tài)(1)轉(zhuǎn)到② 如果上鎖(0)轉(zhuǎn)到⑤
② 上鎖(0)
③ 執(zhí)行代碼塊
④ 執(zhí)行完畢 開鎖(1)
⑤ 線程等待(就緒狀態(tài))
未使用互斥鎖 多線程同時訪問共享資源引起的問題
使用互斥鎖解決多線程訪問共享資源的問題
-
原子屬性
- 屬性中的修飾符:
- nonatomic:非原子屬性。
- atomic:原子屬性(線程安全)蚓曼,針對多線程設(shè)計的亲澡,屬性的修飾符默認值就是atomic。保證同一時間只有一個線程能夠?qū)懭?但是同一時間多個線程都可以取值)纫版。atomic本身就是一把鎖(自旋鎖)床绪。單寫多讀:單個線程寫入,多個線程可以讀取其弊。
- 自旋鎖和互斥鎖:
- 相同點:都能保證同一個時間只有一個線程能夠執(zhí)行鎖定范圍內(nèi)代碼癞己。
- 不同點:①互斥鎖:如果發(fā)現(xiàn)其他線程正在執(zhí)行鎖定代碼,線程會進入休眠(就緒狀態(tài))梭伐,等其它線程時間片到打開鎖后痹雅,線程會被喚醒(執(zhí)行)。
- 自旋鎖:如果發(fā)現(xiàn)有其它線程正在鎖定代碼糊识,線程會用死循環(huán)的方式绩社,一直等待鎖定的代碼執(zhí)行完成,自旋鎖更適合執(zhí)行不耗時的代碼技掏。
- 無論什么鎖铃将,性能都會有損耗(用性能換取數(shù)據(jù)安全性)。
- 屬性中的修飾符:
-
線程安全
- 線程是不安全的哑梳,多個線程同時操作同一個全局變量劲阎。
- 線程安全:在多個線程進行讀寫操作時,仍然能夠保證數(shù)據(jù)的正確鸠真。
- UI線程(主線程):
- 幾乎所有UIKit提供的類都不是線程安全的悯仙。
- 所有更新UI的操作都在主線程上執(zhí)行。
- Mutable線程是不安全的吠卷。